@@ -8,11 +8,11 @@ module EmailConcern |
||
8 | 8 |
end |
9 | 9 |
|
10 | 10 |
def validate_email_options |
11 |
- errors.add(:base, "subject and expected_receive_period_in_days are required") unless options[:subject].present? && options[:expected_receive_period_in_days].present? |
|
11 |
+ errors.add(:base, "subject and expected_receive_period_in_days are required") unless options['subject'].present? && options['expected_receive_period_in_days'].present? |
|
12 | 12 |
end |
13 | 13 |
|
14 | 14 |
def working? |
15 |
- last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago && !recent_error_logs? |
|
15 |
+ last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs? |
|
16 | 16 |
end |
17 | 17 |
|
18 | 18 |
def present(payload) |
@@ -7,20 +7,20 @@ module TwitterConcern |
||
7 | 7 |
end |
8 | 8 |
|
9 | 9 |
def validate_twitter_options |
10 |
- unless options[:consumer_key].present? && |
|
11 |
- options[:consumer_secret].present? && |
|
12 |
- options[:oauth_token].present? && |
|
13 |
- options[:oauth_token_secret].present? |
|
10 |
+ unless options['consumer_key'].present? && |
|
11 |
+ options['consumer_secret'].present? && |
|
12 |
+ options['oauth_token'].present? && |
|
13 |
+ options['oauth_token_secret'].present? |
|
14 | 14 |
errors.add(:base, "consumer_key, consumer_secret, oauth_token and oauth_token_secret are required to authenticate with the Twitter API") |
15 | 15 |
end |
16 | 16 |
end |
17 | 17 |
|
18 | 18 |
def configure_twitter |
19 | 19 |
Twitter.configure do |config| |
20 |
- config.consumer_key = options[:consumer_key] |
|
21 |
- config.consumer_secret = options[:consumer_secret] |
|
22 |
- config.oauth_token = options[:oauth_token] || options[:access_key] |
|
23 |
- config.oauth_token_secret = options[:oauth_token_secret] || options[:access_secret] |
|
20 |
+ config.consumer_key = options['consumer_key'] |
|
21 |
+ config.consumer_secret = options['consumer_secret'] |
|
22 |
+ config.oauth_token = options['oauth_token'] || options['access_key'] |
|
23 |
+ config.oauth_token_secret = options['oauth_token_secret'] || options['access_secret'] |
|
24 | 24 |
end |
25 | 25 |
end |
26 | 26 |
|
@@ -6,19 +6,19 @@ module WeiboConcern |
||
6 | 6 |
end |
7 | 7 |
|
8 | 8 |
def validate_weibo_options |
9 |
- unless options[:app_key].present? && |
|
10 |
- options[:app_secret].present? && |
|
11 |
- options[:access_token].present? |
|
9 |
+ unless options['app_key'].present? && |
|
10 |
+ options['app_secret'].present? && |
|
11 |
+ options['access_token'].present? |
|
12 | 12 |
errors.add(:base, "app_key, app_secret and access_token are required") |
13 | 13 |
end |
14 | 14 |
end |
15 | 15 |
|
16 | 16 |
def weibo_client |
17 | 17 |
unless @weibo_client |
18 |
- WeiboOAuth2::Config.api_key = options[:app_key] # WEIBO_APP_KEY |
|
19 |
- WeiboOAuth2::Config.api_secret = options[:app_secret] # WEIBO_APP_SECRET |
|
18 |
+ WeiboOAuth2::Config.api_key = options['app_key'] # WEIBO_APP_KEY |
|
19 |
+ WeiboOAuth2::Config.api_secret = options['app_secret'] # WEIBO_APP_SECRET |
|
20 | 20 |
@weibo_client = WeiboOAuth2::Client.new |
21 |
- @weibo_client.get_token_from_hash :access_token => options[:access_token] |
|
21 |
+ @weibo_client.get_token_from_hash :access_token => options['access_token'] |
|
22 | 22 |
end |
23 | 23 |
@weibo_client |
24 | 24 |
end |
@@ -1,14 +1,12 @@ |
||
1 |
-require 'serialize_and_normalize' |
|
1 |
+require 'json_with_indifferent_access' |
|
2 | 2 |
require 'assignable_types' |
3 | 3 |
require 'markdown_class_attributes' |
4 | 4 |
require 'utils' |
5 | 5 |
|
6 | 6 |
class Agent < ActiveRecord::Base |
7 |
- include SerializeAndNormalize |
|
8 | 7 |
include AssignableTypes |
9 | 8 |
include MarkdownClassAttributes |
10 | 9 |
|
11 |
- serialize_and_normalize :options, :memory |
|
12 | 10 |
markdown_class_attributes :description, :event_description |
13 | 11 |
|
14 | 12 |
load_types_in "Agents" |
@@ -18,9 +16,21 @@ class Agent < ActiveRecord::Base |
||
18 | 16 |
|
19 | 17 |
attr_accessible :options, :memory, :name, :type, :schedule, :source_ids |
20 | 18 |
|
19 |
+ serialize :options, JSONWithIndifferentAccess |
|
20 |
+ serialize :memory, JSONWithIndifferentAccess |
|
21 |
+ |
|
22 |
+ def options=(o) |
|
23 |
+ self[:options] = ActiveSupport::HashWithIndifferentAccess.new(o) |
|
24 |
+ end |
|
25 |
+ |
|
26 |
+ def memory=(o) |
|
27 |
+ self[:memory] = ActiveSupport::HashWithIndifferentAccess.new(o) |
|
28 |
+ end |
|
29 |
+ |
|
21 | 30 |
validates_presence_of :name, :user |
22 | 31 |
validate :sources_are_owned |
23 | 32 |
validate :validate_schedule |
33 |
+ validate :validate_options |
|
24 | 34 |
|
25 | 35 |
after_initialize :set_default_schedule |
26 | 36 |
before_validation :set_default_schedule |
@@ -74,6 +84,10 @@ class Agent < ActiveRecord::Base |
||
74 | 84 |
raise "Implement me in your subclass" |
75 | 85 |
end |
76 | 86 |
|
87 |
+ def validate_options |
|
88 |
+ # Implement me in your subclass to test for valid options. |
|
89 |
+ end |
|
90 |
+ |
|
77 | 91 |
def event_created_within(days) |
78 | 92 |
event = most_recent_event |
79 | 93 |
event && event.created_at > days.to_i.days.ago && event.payload.present? && event |
@@ -29,18 +29,18 @@ module Agents |
||
29 | 29 |
|
30 | 30 |
def default_options |
31 | 31 |
{ |
32 |
- :start_date => Date.today.httpdate[0..15], |
|
33 |
- :end_date => Date.today.plus_with_duration(100).httpdate[0..15], |
|
34 |
- :from => "New York", |
|
35 |
- :to => "Chicago", |
|
36 |
- :username => "xx", |
|
37 |
- :password => "xx", |
|
38 |
- :expected_update_period_in_days => "1" |
|
32 |
+ 'start_date' => Date.today.httpdate[0..15], |
|
33 |
+ 'end_date' => Date.today.plus_with_duration(100).httpdate[0..15], |
|
34 |
+ 'from' => "New York", |
|
35 |
+ 'to' => "Chicago", |
|
36 |
+ 'username' => "xx", |
|
37 |
+ 'password' => "xx", |
|
38 |
+ 'expected_update_period_in_days' => "1" |
|
39 | 39 |
} |
40 | 40 |
end |
41 | 41 |
|
42 | 42 |
def working? |
43 |
- event_created_within(options[:expected_update_period_in_days]) && !recent_error_logs? |
|
43 |
+ event_created_within(options['expected_update_period_in_days']) && !recent_error_logs? |
|
44 | 44 |
end |
45 | 45 |
|
46 | 46 |
def validate_options |
@@ -54,9 +54,9 @@ module Agents |
||
54 | 54 |
end |
55 | 55 |
|
56 | 56 |
def check |
57 |
- auth_options = {:basic_auth => {:username =>options[:username], :password=>options[:password]}} |
|
58 |
- parse_response = HTTParty.get "http://api.adioso.com/v2/search/parse?q=#{URI.encode(options[:from])}+to+#{URI.encode(options[:to])}", auth_options |
|
59 |
- fare_request = parse_response["search_url"].gsub /(end=)(\d*)([^\d]*)(\d*)/, "\\1#{date_to_unix_epoch(options[:end_date])}\\3#{date_to_unix_epoch(options[:start_date])}" |
|
57 |
+ auth_options = {:basic_auth => {:username =>options[:username], :password=>options['password']}} |
|
58 |
+ parse_response = HTTParty.get "http://api.adioso.com/v2/search/parse?q=#{URI.encode(options['from'])}+to+#{URI.encode(options['to'])}", auth_options |
|
59 |
+ fare_request = parse_response["search_url"].gsub /(end=)(\d*)([^\d]*)(\d*)/, "\\1#{date_to_unix_epoch(options['end_date'])}\\3#{date_to_unix_epoch(options['start_date'])}" |
|
60 | 60 |
fare = HTTParty.get fare_request, auth_options |
61 | 61 |
|
62 | 62 |
if fare["warnings"] |
@@ -64,7 +64,7 @@ module Agents |
||
64 | 64 |
else |
65 | 65 |
event = fare["results"].min {|a,b| a["cost"] <=> b["cost"]} |
66 | 66 |
event["date"] = Time.at(event["date"]).to_date.httpdate[0..15] |
67 |
- event["route"] = "#{options[:from]} to #{options[:to]}" |
|
67 |
+ event["route"] = "#{options['from']} to #{options['to']}" |
|
68 | 68 |
create_event :payload => event |
69 | 69 |
end |
70 | 70 |
end |
@@ -9,7 +9,7 @@ module Agents |
||
9 | 9 |
description <<-MD |
10 | 10 |
The DigestEmailAgent collects any Events sent to it and sends them all via email when run. |
11 | 11 |
The email will be sent to your account's address and will have a `subject` and an optional `headline` before |
12 |
- listing the Events. If the Events' payloads contain a `:message`, that will be highlighted, otherwise everything in |
|
12 |
+ listing the Events. If the Events' payloads contain a `message`, that will be highlighted, otherwise everything in |
|
13 | 13 |
their payloads will be shown. |
14 | 14 |
|
15 | 15 |
Set `expected_receive_period_in_days` to the maximum amount of time that you'd expect to pass between Events being received by this Agent. |
@@ -17,29 +17,29 @@ module Agents |
||
17 | 17 |
|
18 | 18 |
def default_options |
19 | 19 |
{ |
20 |
- :subject => "You have some notifications!", |
|
21 |
- :headline => "Your notifications:", |
|
22 |
- :expected_receive_period_in_days => "2" |
|
20 |
+ 'subject' => "You have some notifications!", |
|
21 |
+ 'headline' => "Your notifications:", |
|
22 |
+ 'expected_receive_period_in_days' => "2" |
|
23 | 23 |
} |
24 | 24 |
end |
25 | 25 |
|
26 | 26 |
def receive(incoming_events) |
27 | 27 |
incoming_events.each do |event| |
28 |
- self.memory[:queue] ||= [] |
|
29 |
- self.memory[:queue] << event.payload |
|
30 |
- self.memory[:events] ||= [] |
|
31 |
- self.memory[:events] << event.id |
|
28 |
+ self.memory['queue'] ||= [] |
|
29 |
+ self.memory['queue'] << event.payload |
|
30 |
+ self.memory['events'] ||= [] |
|
31 |
+ self.memory['events'] << event.id |
|
32 | 32 |
end |
33 | 33 |
end |
34 | 34 |
|
35 | 35 |
def check |
36 |
- if self.memory[:queue] && self.memory[:queue].length > 0 |
|
37 |
- ids = self.memory[:events].join(",") |
|
38 |
- groups = self.memory[:queue].map { |payload| present(payload) } |
|
36 |
+ if self.memory['queue'] && self.memory['queue'].length > 0 |
|
37 |
+ ids = self.memory['events'].join(",") |
|
38 |
+ groups = self.memory['queue'].map { |payload| present(payload) } |
|
39 | 39 |
log "Sending digest mail to #{user.email} with events [#{ids}]" |
40 |
- SystemMailer.delay.send_message(:to => user.email, :subject => options[:subject], :headline => options[:headline], :groups => groups) |
|
41 |
- self.memory[:queue] = [] |
|
42 |
- self.memory[:events] = [] |
|
40 |
+ SystemMailer.delay.send_message(:to => user.email, :subject => options['subject'], :headline => options['headline'], :groups => groups) |
|
41 |
+ self.memory['queue'] = [] |
|
42 |
+ self.memory['events'] = [] |
|
43 | 43 |
end |
44 | 44 |
end |
45 | 45 |
end |
@@ -16,16 +16,16 @@ module Agents |
||
16 | 16 |
|
17 | 17 |
def default_options |
18 | 18 |
{ |
19 |
- :subject => "You have a notification!", |
|
20 |
- :headline => "Your notification:", |
|
21 |
- :expected_receive_period_in_days => "2" |
|
19 |
+ 'subject' => "You have a notification!", |
|
20 |
+ 'headline' => "Your notification:", |
|
21 |
+ 'expected_receive_period_in_days' => "2" |
|
22 | 22 |
} |
23 | 23 |
end |
24 | 24 |
|
25 | 25 |
def receive(incoming_events) |
26 | 26 |
incoming_events.each do |event| |
27 | 27 |
log "Sending digest mail to #{user.email} with event #{event.id}" |
28 |
- SystemMailer.delay.send_message(:to => user.email, :subject => options[:subject], :headline => options[:headline], :groups => [present(event.payload)]) |
|
28 |
+ SystemMailer.delay.send_message(:to => user.email, :subject => options['subject'], :headline => options['headline'], :groups => [present(event.payload)]) |
|
29 | 29 |
end |
30 | 30 |
end |
31 | 31 |
end |
@@ -8,20 +8,20 @@ module Agents |
||
8 | 8 |
For example, here is a possible Event: |
9 | 9 |
|
10 | 10 |
{ |
11 |
- :high => { |
|
12 |
- :celsius => "18", |
|
13 |
- :fahreinheit => "64" |
|
11 |
+ "high": { |
|
12 |
+ "celsius": "18", |
|
13 |
+ "fahreinheit": "64" |
|
14 | 14 |
}, |
15 |
- :conditions => "Rain showers", |
|
16 |
- :data => "This is some data" |
|
15 |
+ "conditions": "Rain showers", |
|
16 |
+ "data": "This is some data" |
|
17 | 17 |
} |
18 | 18 |
|
19 | 19 |
You may want to send this event to another Agent, for example a Twilio Agent, which expects a `message` key. |
20 | 20 |
You can use an Event Formatting Agent's `instructions` setting to do this in the following way: |
21 | 21 |
|
22 |
- instructions: { |
|
23 |
- message: "Today's conditions look like <$.conditions> with a high temperature of <$.high.celsius> degrees Celsius.", |
|
24 |
- subject: "$.data" |
|
22 |
+ "instructions": { |
|
23 |
+ "message": "Today's conditions look like <$.conditions> with a high temperature of <$.high.celsius> degrees Celsius.", |
|
24 |
+ "subject": "$.data" |
|
25 | 25 |
} |
26 | 26 |
|
27 | 27 |
JSONPaths must be between < and > . Make sure that you don't use these symbols anywhere else. |
@@ -29,8 +29,8 @@ module Agents |
||
29 | 29 |
Events generated by this possible Event Formatting Agent will look like: |
30 | 30 |
|
31 | 31 |
{ |
32 |
- :message => "Today's conditions look like Rain showers with a high temperature of 18 degrees Celsius.", |
|
33 |
- :subject => "This is some data" |
|
32 |
+ "message": "Today's conditions look like Rain showers with a high temperature of 18 degrees Celsius.", |
|
33 |
+ "subject": "This is some data" |
|
34 | 34 |
} |
35 | 35 |
|
36 | 36 |
If you want to retain original contents of events and only add new keys, then set `mode` to `merge`, otherwise set it to `clean`. |
@@ -40,25 +40,25 @@ module Agents |
||
40 | 40 |
To CGI escape output (for example when creating a link), prefix with `escape`, like so: |
41 | 41 |
|
42 | 42 |
{ |
43 |
- :message => "A peak was on Twitter in <$.group_by>. Search: https://twitter.com/search?q=<escape $.group_by>" |
|
43 |
+ "message": "A peak was on Twitter in <$.group_by>. Search: https://twitter.com/search?q=<escape $.group_by>" |
|
44 | 44 |
} |
45 | 45 |
MD |
46 | 46 |
|
47 | 47 |
event_description "User defined" |
48 | 48 |
|
49 | 49 |
def validate_options |
50 |
- errors.add(:base, "instructions, mode, skip_agent, and skip_created_at all need to be present.") unless options[:instructions].present? and options[:mode].present? and options[:skip_agent].present? and options[:skip_created_at].present? |
|
50 |
+ errors.add(:base, "instructions, mode, skip_agent, and skip_created_at all need to be present.") unless options['instructions'].present? and options['mode'].present? and options['skip_agent'].present? and options['skip_created_at'].present? |
|
51 | 51 |
end |
52 | 52 |
|
53 | 53 |
def default_options |
54 | 54 |
{ |
55 |
- :instructions => { |
|
56 |
- :message => "You received a text <$.text> from <$.fields.from>", |
|
57 |
- :some_other_field => "Looks like the weather is going to be <$.fields.weather>" |
|
55 |
+ 'instructions' => { |
|
56 |
+ 'message' => "You received a text <$.text> from <$.fields.from>", |
|
57 |
+ 'some_other_field' => "Looks like the weather is going to be <$.fields.weather>" |
|
58 | 58 |
}, |
59 |
- :mode => "clean", |
|
60 |
- :skip_agent => "false", |
|
61 |
- :skip_created_at => "false" |
|
59 |
+ 'mode' => "clean", |
|
60 |
+ 'skip_agent' => "false", |
|
61 |
+ 'skip_created_at' => "false" |
|
62 | 62 |
} |
63 | 63 |
end |
64 | 64 |
|
@@ -68,10 +68,10 @@ module Agents |
||
68 | 68 |
|
69 | 69 |
def receive(incoming_events) |
70 | 70 |
incoming_events.each do |event| |
71 |
- formatted_event = options[:mode].to_s == "merge" ? event.payload : {} |
|
72 |
- options[:instructions].each_pair {|key, value| formatted_event[key] = Utils.interpolate_jsonpaths(value, event.payload) } |
|
73 |
- formatted_event[:agent] = Agent.find(event.agent_id).type.slice!(8..-1) unless options[:skip_agent].to_s == "true" |
|
74 |
- formatted_event[:created_at] = event.created_at unless options[:skip_created_at].to_s == "true" |
|
71 |
+ formatted_event = options['mode'].to_s == "merge" ? event.payload : {} |
|
72 |
+ options['instructions'].each_pair {|key, value| formatted_event[key] = Utils.interpolate_jsonpaths(value, event.payload) } |
|
73 |
+ formatted_event['agent'] = Agent.find(event.agent_id).type.slice!(8..-1) unless options['skip_agent'].to_s == "true" |
|
74 |
+ formatted_event['created_at'] = event.created_at unless options['skip_created_at'].to_s == "true" |
|
75 | 75 |
create_event :payload => formatted_event |
76 | 76 |
end |
77 | 77 |
end |
@@ -74,69 +74,69 @@ module Agents |
||
74 | 74 |
MD |
75 | 75 |
|
76 | 76 |
def validate_options |
77 |
- options[:hit] ||= {} |
|
78 |
- options[:hit][:questions] ||= [] |
|
79 |
- |
|
80 |
- errors.add(:base, "'trigger_on' must be one of 'schedule' or 'event'") unless %w[schedule event].include?(options[:trigger_on]) |
|
81 |
- errors.add(:base, "'hit.assignments' should specify the number of HIT assignments to create") unless options[:hit][:assignments].present? && options[:hit][:assignments].to_i > 0 |
|
82 |
- errors.add(:base, "'hit.title' must be provided") unless options[:hit][:title].present? |
|
83 |
- errors.add(:base, "'hit.description' must be provided") unless options[:hit][:description].present? |
|
84 |
- errors.add(:base, "'hit.questions' must be provided") unless options[:hit][:questions].present? && options[:hit][:questions].length > 0 |
|
85 |
- |
|
86 |
- if options[:trigger_on] == "event" |
|
87 |
- errors.add(:base, "'expected_receive_period_in_days' is required when 'trigger_on' is set to 'event'") unless options[:expected_receive_period_in_days].present? |
|
88 |
- elsif options[:trigger_on] == "schedule" |
|
89 |
- errors.add(:base, "'submission_period' must be set to a positive number of hours when 'trigger_on' is set to 'schedule'") unless options[:submission_period].present? && options[:submission_period].to_i > 0 |
|
77 |
+ options['hit'] ||= {} |
|
78 |
+ options['hit']['questions'] ||= [] |
|
79 |
+ |
|
80 |
+ errors.add(:base, "'trigger_on' must be one of 'schedule' or 'event'") unless %w[schedule event].include?(options['trigger_on']) |
|
81 |
+ errors.add(:base, "'hit.assignments' should specify the number of HIT assignments to create") unless options['hit']['assignments'].present? && options['hit']['assignments'].to_i > 0 |
|
82 |
+ errors.add(:base, "'hit.title' must be provided") unless options['hit']['title'].present? |
|
83 |
+ errors.add(:base, "'hit.description' must be provided") unless options['hit']['description'].present? |
|
84 |
+ errors.add(:base, "'hit.questions' must be provided") unless options['hit']['questions'].present? && options['hit']['questions'].length > 0 |
|
85 |
+ |
|
86 |
+ if options['trigger_on'] == "event" |
|
87 |
+ errors.add(:base, "'expected_receive_period_in_days' is required when 'trigger_on' is set to 'event'") unless options['expected_receive_period_in_days'].present? |
|
88 |
+ elsif options['trigger_on'] == "schedule" |
|
89 |
+ errors.add(:base, "'submission_period' must be set to a positive number of hours when 'trigger_on' is set to 'schedule'") unless options['submission_period'].present? && options['submission_period'].to_i > 0 |
|
90 | 90 |
end |
91 | 91 |
|
92 |
- if options[:hit][:questions].any? { |question| [:key, :name, :required, :type, :question].any? {|k| !question[k].present? } } |
|
92 |
+ if options['hit']['questions'].any? { |question| %w[key name required type question].any? {|k| !question[k].present? } } |
|
93 | 93 |
errors.add(:base, "all questions must set 'key', 'name', 'required', 'type', and 'question'") |
94 | 94 |
end |
95 | 95 |
|
96 |
- if options[:hit][:questions].any? { |question| question[:type] == "selection" && (!question[:selections].present? || question[:selections].length == 0 || !question[:selections].all? {|s| s[:key].present? } || !question[:selections].all? { |s| s[:text].present? })} |
|
96 |
+ if options['hit']['questions'].any? { |question| question['type'] == "selection" && (!question['selections'].present? || question['selections'].length == 0 || !question['selections'].all? {|s| s['key'].present? } || !question['selections'].all? { |s| s['text'].present? })} |
|
97 | 97 |
errors.add(:base, "all questions of type 'selection' must have a selections array with selections that set 'key' and 'name'") |
98 | 98 |
end |
99 | 99 |
|
100 |
- if options[:take_majority] == "true" && options[:hit][:questions].any? { |question| question[:type] != "selection" } |
|
100 |
+ if options['take_majority'] == "true" && options['hit']['questions'].any? { |question| question['type'] != "selection" } |
|
101 | 101 |
errors.add(:base, "all questions must be of type 'selection' to use the 'take_majority' option") |
102 | 102 |
end |
103 | 103 |
end |
104 | 104 |
|
105 | 105 |
def default_options |
106 | 106 |
{ |
107 |
- :expected_receive_period_in_days => 2, |
|
108 |
- :trigger_on => "event", |
|
109 |
- :hit => |
|
107 |
+ 'expected_receive_period_in_days' => 2, |
|
108 |
+ 'trigger_on' => "event", |
|
109 |
+ 'hit' => |
|
110 | 110 |
{ |
111 |
- :assignments => 1, |
|
112 |
- :title => "Sentiment evaluation", |
|
113 |
- :description => "Please rate the sentiment of this message: '<$.message>'", |
|
114 |
- :reward => 0.05, |
|
115 |
- :lifetime_in_seconds => 24 * 60 * 60, |
|
116 |
- :questions => |
|
111 |
+ 'assignments' => 1, |
|
112 |
+ 'title' => "Sentiment evaluation", |
|
113 |
+ 'description' => "Please rate the sentiment of this message: '<$.message>'", |
|
114 |
+ 'reward' => 0.05, |
|
115 |
+ 'lifetime_in_seconds' => 24 * 60 * 60, |
|
116 |
+ 'questions' => |
|
117 | 117 |
[ |
118 | 118 |
{ |
119 |
- :type => "selection", |
|
120 |
- :key => "sentiment", |
|
121 |
- :name => "Sentiment", |
|
122 |
- :required => "true", |
|
123 |
- :question => "Please select the best sentiment value:", |
|
124 |
- :selections => |
|
119 |
+ 'type' => "selection", |
|
120 |
+ 'key' => "sentiment", |
|
121 |
+ 'name' => "Sentiment", |
|
122 |
+ 'required' => "true", |
|
123 |
+ 'question' => "Please select the best sentiment value:", |
|
124 |
+ 'selections' => |
|
125 | 125 |
[ |
126 |
- { :key => "happy", :text => "Happy" }, |
|
127 |
- { :key => "sad", :text => "Sad" }, |
|
128 |
- { :key => "neutral", :text => "Neutral" } |
|
126 |
+ { 'key' => "happy", 'text' => "Happy" }, |
|
127 |
+ { 'key' => "sad", 'text' => "Sad" }, |
|
128 |
+ { 'key' => "neutral", 'text' => "Neutral" } |
|
129 | 129 |
] |
130 | 130 |
}, |
131 | 131 |
{ |
132 |
- :type => "free_text", |
|
133 |
- :key => "feedback", |
|
134 |
- :name => "Have any feedback for us?", |
|
135 |
- :required => "false", |
|
136 |
- :question => "Feedback", |
|
137 |
- :default => "Type here...", |
|
138 |
- :min_length => "2", |
|
139 |
- :max_length => "2000" |
|
132 |
+ 'type' => "free_text", |
|
133 |
+ 'key' => "feedback", |
|
134 |
+ 'name' => "Have any feedback for us?", |
|
135 |
+ 'required' => "false", |
|
136 |
+ 'question' => "Feedback", |
|
137 |
+ 'default' => "Type here...", |
|
138 |
+ 'min_length' => "2", |
|
139 |
+ 'max_length' => "2000" |
|
140 | 140 |
} |
141 | 141 |
] |
142 | 142 |
} |
@@ -144,20 +144,20 @@ module Agents |
||
144 | 144 |
end |
145 | 145 |
|
146 | 146 |
def working? |
147 |
- last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago && !recent_error_logs? |
|
147 |
+ last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs? |
|
148 | 148 |
end |
149 | 149 |
|
150 | 150 |
def check |
151 | 151 |
review_hits |
152 | 152 |
|
153 |
- if options[:trigger_on] == "schedule" && (memory[:last_schedule] || 0) <= Time.now.to_i - options[:submission_period].to_i * 60 * 60 |
|
154 |
- memory[:last_schedule] = Time.now.to_i |
|
153 |
+ if options['trigger_on'] == "schedule" && (memory['last_schedule'] || 0) <= Time.now.to_i - options['submission_period'].to_i * 60 * 60 |
|
154 |
+ memory['last_schedule'] = Time.now.to_i |
|
155 | 155 |
create_hit |
156 | 156 |
end |
157 | 157 |
end |
158 | 158 |
|
159 | 159 |
def receive(incoming_events) |
160 |
- if options[:trigger_on] == "event" |
|
160 |
+ if options['trigger_on'] == "event" |
|
161 | 161 |
incoming_events.each do |event| |
162 | 162 |
create_hit event |
163 | 163 |
end |
@@ -168,7 +168,7 @@ module Agents |
||
168 | 168 |
|
169 | 169 |
def review_hits |
170 | 170 |
reviewable_hit_ids = RTurk::GetReviewableHITs.create.hit_ids |
171 |
- my_reviewed_hit_ids = reviewable_hit_ids & (memory[:hits] || {}).keys.map(&:to_s) |
|
171 |
+ my_reviewed_hit_ids = reviewable_hit_ids & (memory['hits'] || {}).keys |
|
172 | 172 |
if reviewable_hit_ids.length > 0 |
173 | 173 |
log "MTurk reports #{reviewable_hit_ids.length} HITs, of which I own [#{my_reviewed_hit_ids.to_sentence}]" |
174 | 174 |
end |
@@ -178,26 +178,26 @@ module Agents |
||
178 | 178 |
|
179 | 179 |
log "Looking at HIT #{hit_id}. I found #{assignments.length} assignments#{" with the statuses: #{assignments.map(&:status).to_sentence}" if assignments.length > 0}" |
180 | 180 |
if assignments.length == hit.max_assignments && assignments.all? { |assignment| assignment.status == "Submitted" } |
181 |
- payload = { :answers => assignments.map(&:answers) } |
|
181 |
+ payload = { 'answers' => assignments.map(&:answers) } |
|
182 | 182 |
|
183 |
- if options[:take_majority] == "true" |
|
183 |
+ if options['take_majority'] == "true" |
|
184 | 184 |
counts = {} |
185 |
- options[:hit][:questions].each do |question| |
|
186 |
- question_counts = question[:selections].inject({}) { |memo, selection| memo[selection[:key]] = 0; memo } |
|
185 |
+ options['hit']['questions'].each do |question| |
|
186 |
+ question_counts = question['selections'].inject({}) { |memo, selection| memo[selection['key']] = 0; memo } |
|
187 | 187 |
assignments.each do |assignment| |
188 | 188 |
answers = ActiveSupport::HashWithIndifferentAccess.new(assignment.answers) |
189 |
- answer = answers[question[:key]] |
|
189 |
+ answer = answers[question['key']] |
|
190 | 190 |
question_counts[answer] += 1 |
191 | 191 |
end |
192 |
- counts[question[:key]] = question_counts |
|
192 |
+ counts[question['key']] = question_counts |
|
193 | 193 |
end |
194 |
- payload[:counts] = counts |
|
194 |
+ payload['counts'] = counts |
|
195 | 195 |
|
196 | 196 |
majority_answer = counts.inject({}) do |memo, (key, question_counts)| |
197 | 197 |
memo[key] = question_counts.to_a.sort {|a, b| a.last <=> b.last }.last.first |
198 | 198 |
memo |
199 | 199 |
end |
200 |
- payload[:majority_answer] = majority_answer |
|
200 |
+ payload['majority_answer'] = majority_answer |
|
201 | 201 |
|
202 | 202 |
if all_questions_are_numeric? |
203 | 203 |
average_answer = counts.inject({}) do |memo, (key, question_counts)| |
@@ -209,44 +209,44 @@ module Agents |
||
209 | 209 |
memo[key] = sum / divisor.to_f |
210 | 210 |
memo |
211 | 211 |
end |
212 |
- payload[:average_answer] = average_answer |
|
212 |
+ payload['average_answer'] = average_answer |
|
213 | 213 |
end |
214 | 214 |
end |
215 | 215 |
|
216 | 216 |
event = create_event :payload => payload |
217 |
- log "Event emitted with answer(s)", :outbound_event => event, :inbound_event => Event.find_by_id(memory[:hits][hit_id]) |
|
217 |
+ log "Event emitted with answer(s)", :outbound_event => event, :inbound_event => Event.find_by_id(memory['hits'][hit_id]) |
|
218 | 218 |
|
219 | 219 |
assignments.each(&:approve!) |
220 | 220 |
hit.dispose! |
221 | 221 |
|
222 |
- memory[:hits].delete(hit_id) |
|
222 |
+ memory['hits'].delete(hit_id) |
|
223 | 223 |
end |
224 | 224 |
end |
225 | 225 |
end |
226 | 226 |
|
227 | 227 |
def all_questions_are_numeric? |
228 |
- options[:hit][:questions].all? do |question| |
|
229 |
- question[:selections].all? do |selection| |
|
230 |
- selection[:key] == selection[:key].to_f.to_s || selection[:key] == selection[:key].to_i.to_s |
|
228 |
+ options['hit']['questions'].all? do |question| |
|
229 |
+ question['selections'].all? do |selection| |
|
230 |
+ selection['key'] == selection['key'].to_f.to_s || selection['key'] == selection['key'].to_i.to_s |
|
231 | 231 |
end |
232 | 232 |
end |
233 | 233 |
end |
234 | 234 |
|
235 | 235 |
def create_hit(event = nil) |
236 | 236 |
payload = event ? event.payload : {} |
237 |
- title = Utils.interpolate_jsonpaths(options[:hit][:title], payload).strip |
|
238 |
- description = Utils.interpolate_jsonpaths(options[:hit][:description], payload).strip |
|
239 |
- questions = Utils.recursively_interpolate_jsonpaths(options[:hit][:questions], payload) |
|
237 |
+ title = Utils.interpolate_jsonpaths(options['hit']['title'], payload).strip |
|
238 |
+ description = Utils.interpolate_jsonpaths(options['hit']['description'], payload).strip |
|
239 |
+ questions = Utils.recursively_interpolate_jsonpaths(options['hit']['questions'], payload) |
|
240 | 240 |
hit = RTurk::Hit.create(:title => title) do |hit| |
241 |
- hit.max_assignments = (options[:hit][:assignments] || 1).to_i |
|
241 |
+ hit.max_assignments = (options['hit']['assignments'] || 1).to_i |
|
242 | 242 |
hit.description = description |
243 |
- hit.lifetime = (options[:hit][:lifetime_in_seconds] || 24 * 60 * 60).to_i |
|
243 |
+ hit.lifetime = (options['hit']['lifetime_in_seconds'] || 24 * 60 * 60).to_i |
|
244 | 244 |
hit.question_form AgentQuestionForm.new(:title => title, :description => description, :questions => questions) |
245 |
- hit.reward = (options[:hit][:reward] || 0.05).to_f |
|
245 |
+ hit.reward = (options['hit']['reward'] || 0.05).to_f |
|
246 | 246 |
#hit.qualifications.add :approval_rate, { :gt => 80 } |
247 | 247 |
end |
248 |
- memory[:hits] ||= {} |
|
249 |
- memory[:hits][hit.id] = event && event.id |
|
248 |
+ memory['hits'] ||= {} |
|
249 |
+ memory['hits'][hit.id] = event && event.id |
|
250 | 250 |
log "HIT created with ID #{hit.id} and URL #{hit.url}", :inbound_event => event |
251 | 251 |
end |
252 | 252 |
|
@@ -314,7 +314,7 @@ module Agents |
||
314 | 314 |
end |
315 | 315 |
end |
316 | 316 |
|
317 |
- if question[:default].present? |
|
317 |
+ if question['default'].present? |
|
318 | 318 |
DefaultText do |
319 | 319 |
text question['default'] |
320 | 320 |
end |
@@ -14,8 +14,8 @@ module Agents |
||
14 | 14 |
end |
15 | 15 |
|
16 | 16 |
def handle_details_post(params) |
17 |
- if params[:payload] |
|
18 |
- create_event(:payload => params[:payload]) |
|
17 |
+ if params['payload'] |
|
18 |
+ create_event(:payload => params['payload']) |
|
19 | 19 |
{ :success => true } |
20 | 20 |
else |
21 | 21 |
{ :success => false, :error => "You must provide a JSON payload" } |
@@ -28,22 +28,22 @@ module Agents |
||
28 | 28 |
MD |
29 | 29 |
|
30 | 30 |
def validate_options |
31 |
- unless options[:expected_receive_period_in_days].present? && options[:message].present? && options[:value_path].present? |
|
31 |
+ unless options['expected_receive_period_in_days'].present? && options['message'].present? && options['value_path'].present? |
|
32 | 32 |
errors.add(:base, "expected_receive_period_in_days, value_path, and message are required") |
33 | 33 |
end |
34 | 34 |
end |
35 | 35 |
|
36 | 36 |
def default_options |
37 | 37 |
{ |
38 |
- :expected_receive_period_in_days => "2", |
|
39 |
- :group_by_path => "filter", |
|
40 |
- :value_path => "count", |
|
41 |
- :message => "A peak was found" |
|
38 |
+ 'expected_receive_period_in_days' => "2", |
|
39 |
+ 'group_by_path' => "filter", |
|
40 |
+ 'value_path' => "count", |
|
41 |
+ 'message' => "A peak was found" |
|
42 | 42 |
} |
43 | 43 |
end |
44 | 44 |
|
45 | 45 |
def working? |
46 |
- last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago && !recent_error_logs? |
|
46 |
+ last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs? |
|
47 | 47 |
end |
48 | 48 |
|
49 | 49 |
def receive(incoming_events) |
@@ -57,25 +57,25 @@ module Agents |
||
57 | 57 |
private |
58 | 58 |
|
59 | 59 |
def check_for_peak(group, event) |
60 |
- memory[:peaks] ||= {} |
|
61 |
- memory[:peaks][group] ||= [] |
|
60 |
+ memory['peaks'] ||= {} |
|
61 |
+ memory['peaks'][group] ||= [] |
|
62 | 62 |
|
63 |
- if memory[:data][group].length > 4 && (memory[:peaks][group].empty? || memory[:peaks][group].last < event.created_at.to_i - peak_spacing) |
|
63 |
+ if memory['data'][group].length > 4 && (memory['peaks'][group].empty? || memory['peaks'][group].last < event.created_at.to_i - peak_spacing) |
|
64 | 64 |
average_value, standard_deviation = stats_for(group, :skip_last => 1) |
65 |
- newest_value, newest_time = memory[:data][group][-1].map(&:to_f) |
|
65 |
+ newest_value, newest_time = memory['data'][group][-1].map(&:to_f) |
|
66 | 66 |
|
67 | 67 |
#p [newest_value, average_value, average_value + std_multiple * standard_deviation, standard_deviation] |
68 | 68 |
|
69 | 69 |
if newest_value > average_value + std_multiple * standard_deviation |
70 |
- memory[:peaks][group] << newest_time |
|
71 |
- memory[:peaks][group].reject! { |p| p <= newest_time - window_duration } |
|
72 |
- create_event :payload => {:message => options[:message], :peak => newest_value, :peak_time => newest_time, :grouped_by => group.to_s} |
|
70 |
+ memory['peaks'][group] << newest_time |
|
71 |
+ memory['peaks'][group].reject! { |p| p <= newest_time - window_duration } |
|
72 |
+ create_event :payload => { 'message' => options['message'], 'peak' => newest_value, 'peak_time' => newest_time, 'grouped_by' => group.to_s } |
|
73 | 73 |
end |
74 | 74 |
end |
75 | 75 |
end |
76 | 76 |
|
77 | 77 |
def stats_for(group, options = {}) |
78 |
- data = memory[:data][group].map { |d| d.first.to_f } |
|
78 |
+ data = memory['data'][group].map { |d| d.first.to_f } |
|
79 | 79 |
data = data[0...(data.length - (options[:skip_last] || 0))] |
80 | 80 |
length = data.length.to_f |
81 | 81 |
mean = 0 |
@@ -94,39 +94,39 @@ module Agents |
||
94 | 94 |
end |
95 | 95 |
|
96 | 96 |
def window_duration |
97 |
- if options[:window_duration].present? # The older option |
|
98 |
- options[:window_duration].to_i |
|
97 |
+ if options['window_duration'].present? # The older option |
|
98 |
+ options['window_duration'].to_i |
|
99 | 99 |
else |
100 |
- (options[:window_duration_in_days] || 14).to_f.days |
|
100 |
+ (options['window_duration_in_days'] || 14).to_f.days |
|
101 | 101 |
end |
102 | 102 |
end |
103 | 103 |
|
104 | 104 |
def std_multiple |
105 |
- (options[:std_multiple] || 3).to_f |
|
105 |
+ (options['std_multiple'] || 3).to_f |
|
106 | 106 |
end |
107 | 107 |
|
108 | 108 |
def peak_spacing |
109 |
- if options[:peak_spacing].present? # The older option |
|
110 |
- options[:peak_spacing].to_i |
|
109 |
+ if options['peak_spacing'].present? # The older option |
|
110 |
+ options['peak_spacing'].to_i |
|
111 | 111 |
else |
112 |
- (options[:min_peak_spacing_in_days] || 2).to_f.days |
|
112 |
+ (options['min_peak_spacing_in_days'] || 2).to_f.days |
|
113 | 113 |
end |
114 | 114 |
end |
115 | 115 |
|
116 | 116 |
def group_for(event) |
117 |
- ((options[:group_by_path].present? && Utils.value_at(event.payload, options[:group_by_path])) || 'no_group') |
|
117 |
+ ((options['group_by_path'].present? && Utils.value_at(event.payload, options['group_by_path'])) || 'no_group') |
|
118 | 118 |
end |
119 | 119 |
|
120 | 120 |
def remember(group, event) |
121 |
- memory[:data] ||= {} |
|
122 |
- memory[:data][group] ||= [] |
|
123 |
- memory[:data][group] << [Utils.value_at(event.payload, options[:value_path]), event.created_at.to_i] |
|
121 |
+ memory['data'] ||= {} |
|
122 |
+ memory['data'][group] ||= [] |
|
123 |
+ memory['data'][group] << [ Utils.value_at(event.payload, options['value_path']), event.created_at.to_i ] |
|
124 | 124 |
cleanup group |
125 | 125 |
end |
126 | 126 |
|
127 | 127 |
def cleanup(group) |
128 |
- newest_time = memory[:data][group].last.last |
|
129 |
- memory[:data][group].reject! { |value, time| time <= newest_time - window_duration } |
|
128 |
+ newest_time = memory['data'][group].last.last |
|
129 |
+ memory['data'][group].reject! { |value, time| time <= newest_time - window_duration } |
|
130 | 130 |
end |
131 | 131 |
end |
132 | 132 |
end |
@@ -11,17 +11,17 @@ module Agents |
||
11 | 11 |
|
12 | 12 |
def default_options |
13 | 13 |
{ |
14 |
- :post_url => "http://www.example.com", |
|
15 |
- :expected_receive_period_in_days => 1 |
|
14 |
+ 'post_url' => "http://www.example.com", |
|
15 |
+ 'expected_receive_period_in_days' => 1 |
|
16 | 16 |
} |
17 | 17 |
end |
18 | 18 |
|
19 | 19 |
def working? |
20 |
- last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago && !recent_error_logs? |
|
20 |
+ last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs? |
|
21 | 21 |
end |
22 | 22 |
|
23 | 23 |
def validate_options |
24 |
- unless options[:post_url].present? && options[:expected_receive_period_in_days].present? |
|
24 |
+ unless options['post_url'].present? && options['expected_receive_period_in_days'].present? |
|
25 | 25 |
errors.add(:base, "post_url and expected_receive_period_in_days are required fields") |
26 | 26 |
end |
27 | 27 |
end |
@@ -28,31 +28,31 @@ module Agents |
||
28 | 28 |
|
29 | 29 |
def default_options |
30 | 30 |
{ |
31 |
- :content => "$.message.text[*]", |
|
32 |
- :expected_receive_period_in_days => 1 |
|
31 |
+ 'content' => "$.message.text[*]", |
|
32 |
+ 'expected_receive_period_in_days' => 1 |
|
33 | 33 |
} |
34 | 34 |
end |
35 | 35 |
|
36 | 36 |
def working? |
37 |
- last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago && !recent_error_logs? |
|
37 |
+ last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs? |
|
38 | 38 |
end |
39 | 39 |
|
40 | 40 |
def receive(incoming_events) |
41 | 41 |
anew = self.class.sentiment_hash |
42 | 42 |
incoming_events.each do |event| |
43 |
- Utils.values_at(event.payload, options[:content]).each do |content| |
|
43 |
+ Utils.values_at(event.payload, options['content']).each do |content| |
|
44 | 44 |
sent_values = sentiment_values anew, content |
45 |
- create_event :payload => { :content => content, |
|
46 |
- :valence => sent_values[0], |
|
47 |
- :arousal => sent_values[1], |
|
48 |
- :dominance => sent_values[2], |
|
49 |
- :original_event => event.payload } |
|
45 |
+ create_event :payload => { 'content' => content, |
|
46 |
+ 'valence' => sent_values[0], |
|
47 |
+ 'arousal' => sent_values[1], |
|
48 |
+ 'dominance' => sent_values[2], |
|
49 |
+ 'original_event' => event.payload } |
|
50 | 50 |
end |
51 | 51 |
end |
52 | 52 |
end |
53 | 53 |
|
54 | 54 |
def validate_options |
55 |
- errors.add(:base, "content and expected_receive_period_in_days must be present") unless options[:content].present? && options[:expected_receive_period_in_days].present? |
|
55 |
+ errors.add(:base, "content and expected_receive_period_in_days must be present") unless options['content'].present? && options['expected_receive_period_in_days'].present? |
|
56 | 56 |
end |
57 | 57 |
|
58 | 58 |
def self.sentiment_hash |
@@ -17,26 +17,26 @@ module Agents |
||
17 | 17 |
|
18 | 18 |
def default_options |
19 | 19 |
{ |
20 |
- :client_id => "xxxxxx", |
|
21 |
- :client_secret => "xxxxxx", |
|
22 |
- :to => "fi", |
|
23 |
- :expected_receive_period_in_days => 1, |
|
24 |
- :content => { |
|
25 |
- :text => "$.message.text", |
|
26 |
- :content => "$.xyz" |
|
20 |
+ 'client_id' => "xxxxxx", |
|
21 |
+ 'client_secret' => "xxxxxx", |
|
22 |
+ 'to' => "fi", |
|
23 |
+ 'expected_receive_period_in_days' => 1, |
|
24 |
+ 'content' => { |
|
25 |
+ 'text' => "$.message.text", |
|
26 |
+ 'content' => "$.xyz" |
|
27 | 27 |
} |
28 | 28 |
} |
29 | 29 |
end |
30 | 30 |
|
31 | 31 |
def working? |
32 |
- last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago && !recent_error_logs? |
|
32 |
+ last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs? |
|
33 | 33 |
end |
34 | 34 |
|
35 | 35 |
def translate(text, to, access_token) |
36 | 36 |
translate_uri = URI 'http://api.microsofttranslator.com/v2/Ajax.svc/Translate' |
37 | 37 |
params = { |
38 |
- :text => text, |
|
39 |
- :to => to |
|
38 |
+ 'text' => text, |
|
39 |
+ 'to' => to |
|
40 | 40 |
} |
41 | 41 |
translate_uri.query = URI.encode_www_form params |
42 | 42 |
request = Net::HTTP::Get.new translate_uri.request_uri |
@@ -47,7 +47,7 @@ module Agents |
||
47 | 47 |
end |
48 | 48 |
|
49 | 49 |
def validate_options |
50 |
- unless options[:client_id].present? && options[:client_secret].present? && options[:to].present? && options[:content].present? && options[:expected_receive_period_in_days].present? |
|
50 |
+ unless options['client_id'].present? && options['client_secret'].present? && options['to'].present? && options['content'].present? && options['expected_receive_period_in_days'].present? |
|
51 | 51 |
errors.add :base, "client_id,client_secret,to,expected_receive_period_in_days and content are all required" |
52 | 52 |
end |
53 | 53 |
end |
@@ -60,16 +60,16 @@ module Agents |
||
60 | 60 |
|
61 | 61 |
def receive(incoming_events) |
62 | 62 |
auth_uri = URI "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13" |
63 |
- response = postform auth_uri, :client_id => options[:client_id], |
|
64 |
- :client_secret => options[:client_secret], |
|
63 |
+ response = postform auth_uri, :client_id => options['client_id'], |
|
64 |
+ :client_secret => options['client_secret'], |
|
65 | 65 |
:scope => "http://api.microsofttranslator.com", |
66 | 66 |
:grant_type => "client_credentials" |
67 | 67 |
access_token = JSON.parse(response.body)["access_token"] |
68 | 68 |
incoming_events.each do |event| |
69 | 69 |
translated_event = {} |
70 |
- options[:content].each_pair do |key, value| |
|
70 |
+ options['content'].each_pair do |key, value| |
|
71 | 71 |
to_be_translated = Utils.values_at event.payload, value |
72 |
- translated_event[key] = translate to_be_translated.first, options[:to], access_token |
|
72 |
+ translated_event[key] = translate to_be_translated.first, options['to'], access_token |
|
73 | 73 |
end |
74 | 74 |
create_event :payload => translated_event |
75 | 75 |
end |
@@ -23,57 +23,57 @@ module Agents |
||
23 | 23 |
MD |
24 | 24 |
|
25 | 25 |
def validate_options |
26 |
- unless options[:expected_receive_period_in_days].present? && options[:message].present? && options[:rules].present? && |
|
27 |
- options[:rules].all? { |rule| rule[:type].present? && VALID_COMPARISON_TYPES.include?(rule[:type]) && rule[:value].present? && rule[:path].present? } |
|
26 |
+ unless options['expected_receive_period_in_days'].present? && options['message'].present? && options['rules'].present? && |
|
27 |
+ options['rules'].all? { |rule| rule['type'].present? && VALID_COMPARISON_TYPES.include?(rule['type']) && rule['value'].present? && rule['path'].present? } |
|
28 | 28 |
errors.add(:base, "expected_receive_period_in_days, message, and rules, with a type, value, and path for every rule, are required") |
29 | 29 |
end |
30 | 30 |
end |
31 | 31 |
|
32 | 32 |
def default_options |
33 | 33 |
{ |
34 |
- :expected_receive_period_in_days => "2", |
|
35 |
- :rules => [{ |
|
36 |
- :type => "regex", |
|
37 |
- :value => "foo\\d+bar", |
|
38 |
- :path => "topkey.subkey.subkey.goal", |
|
39 |
- }], |
|
40 |
- :message => "Looks like your pattern matched in '<value>'!" |
|
34 |
+ 'expected_receive_period_in_days' => "2", |
|
35 |
+ 'rules' => [{ |
|
36 |
+ 'type' => "regex", |
|
37 |
+ 'value' => "foo\\d+bar", |
|
38 |
+ 'path' => "topkey.subkey.subkey.goal", |
|
39 |
+ }], |
|
40 |
+ 'message' => "Looks like your pattern matched in '<value>'!" |
|
41 | 41 |
} |
42 | 42 |
end |
43 | 43 |
|
44 | 44 |
def working? |
45 |
- last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago && !recent_error_logs? |
|
45 |
+ last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs? |
|
46 | 46 |
end |
47 | 47 |
|
48 | 48 |
def receive(incoming_events) |
49 | 49 |
incoming_events.each do |event| |
50 |
- match = options[:rules].all? do |rule| |
|
51 |
- value_at_path = Utils.value_at(event[:payload], rule[:path]) |
|
52 |
- case rule[:type] |
|
50 |
+ match = options['rules'].all? do |rule| |
|
51 |
+ value_at_path = Utils.value_at(event['payload'], rule['path']) |
|
52 |
+ case rule['type'] |
|
53 | 53 |
when "regex" |
54 |
- value_at_path.to_s =~ Regexp.new(rule[:value], Regexp::IGNORECASE) |
|
54 |
+ value_at_path.to_s =~ Regexp.new(rule['value'], Regexp::IGNORECASE) |
|
55 | 55 |
when "!regex" |
56 |
- value_at_path.to_s !~ Regexp.new(rule[:value], Regexp::IGNORECASE) |
|
56 |
+ value_at_path.to_s !~ Regexp.new(rule['value'], Regexp::IGNORECASE) |
|
57 | 57 |
when "field>value" |
58 |
- value_at_path.to_f > rule[:value].to_f |
|
58 |
+ value_at_path.to_f > rule['value'].to_f |
|
59 | 59 |
when "field>=value" |
60 |
- value_at_path.to_f >= rule[:value].to_f |
|
60 |
+ value_at_path.to_f >= rule['value'].to_f |
|
61 | 61 |
when "field<value" |
62 |
- value_at_path.to_f < rule[:value].to_f |
|
62 |
+ value_at_path.to_f < rule['value'].to_f |
|
63 | 63 |
when "field<=value" |
64 |
- value_at_path.to_f <= rule[:value].to_f |
|
64 |
+ value_at_path.to_f <= rule['value'].to_f |
|
65 | 65 |
when "field==value" |
66 |
- value_at_path.to_s == rule[:value].to_s |
|
66 |
+ value_at_path.to_s == rule['value'].to_s |
|
67 | 67 |
when "field!=value" |
68 |
- value_at_path.to_s != rule[:value].to_s |
|
68 |
+ value_at_path.to_s != rule['value'].to_s |
|
69 | 69 |
else |
70 |
- raise "Invalid :type of #{rule[:type]} in TriggerAgent##{id}" |
|
70 |
+ raise "Invalid type of #{rule['type']} in TriggerAgent##{id}" |
|
71 | 71 |
end |
72 | 72 |
end |
73 | 73 |
|
74 | 74 |
if match |
75 |
- create_event :payload => { :message => make_message(event[:payload]) } # Maybe this should include the |
|
76 |
- # original event as well? |
|
75 |
+ create_event :payload => { 'message' => make_message(event[:payload]) } # Maybe this should include the |
|
76 |
+ # original event as well? |
|
77 | 77 |
end |
78 | 78 |
end |
79 | 79 |
end |
@@ -9,7 +9,7 @@ module Agents |
||
9 | 9 |
description <<-MD |
10 | 10 |
The TwilioAgent receives and collects events and sends them via text message or gives you a call when scheduled. |
11 | 11 |
|
12 |
- It is assumed that events have a `:message`, `:text`, or `:sms` key, the value of which is sent as the content of the text message/call. You can use Event Formatting Agent if your event does not provide these keys. |
|
12 |
+ It is assumed that events have a `message`, `text`, or `sms` key, the value of which is sent as the content of the text message/call. You can use Event Formatting Agent if your event does not provide these keys. |
|
13 | 13 |
|
14 | 14 |
Set `receiver_cell` to the number to receive text messages/call and `sender_cell` to the number sending them. |
15 | 15 |
|
@@ -22,35 +22,35 @@ module Agents |
||
22 | 22 |
|
23 | 23 |
def default_options |
24 | 24 |
{ |
25 |
- :account_sid => 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
|
26 |
- :auth_token => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
|
27 |
- :sender_cell => 'xxxxxxxxxx', |
|
28 |
- :receiver_cell => 'xxxxxxxxxx', |
|
29 |
- :server_url => 'http://somename.com:3000', |
|
30 |
- :receive_text => 'true', |
|
31 |
- :receive_call => 'false', |
|
32 |
- :expected_receive_period_in_days => '1' |
|
25 |
+ 'account_sid' => 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
|
26 |
+ 'auth_token' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
|
27 |
+ 'sender_cell' => 'xxxxxxxxxx', |
|
28 |
+ 'receiver_cell' => 'xxxxxxxxxx', |
|
29 |
+ 'server_url' => 'http://somename.com:3000', |
|
30 |
+ 'receive_text' => 'true', |
|
31 |
+ 'receive_call' => 'false', |
|
32 |
+ 'expected_receive_period_in_days' => '1' |
|
33 | 33 |
} |
34 | 34 |
end |
35 | 35 |
|
36 | 36 |
def validate_options |
37 |
- unless options[:account_sid].present? && options[:auth_token].present? && options[:sender_cell].present? && options[:receiver_cell].present? && options[:expected_receive_period_in_days].present? && options[:receive_call].present? && options[:receive_text].present? |
|
37 |
+ unless options['account_sid'].present? && options['auth_token'].present? && options['sender_cell'].present? && options['receiver_cell'].present? && options['expected_receive_period_in_days'].present? && options['receive_call'].present? && options['receive_text'].present? |
|
38 | 38 |
errors.add(:base, 'account_sid, auth_token, sender_cell, receiver_cell, receive_text, receive_call and expected_receive_period_in_days are all required') |
39 | 39 |
end |
40 | 40 |
end |
41 | 41 |
|
42 | 42 |
def receive(incoming_events) |
43 |
- @client = Twilio::REST::Client.new options[:account_sid], options[:auth_token] |
|
44 |
- memory[:pending_calls] ||= {} |
|
43 |
+ @client = Twilio::REST::Client.new options['account_sid'], options['auth_token'] |
|
44 |
+ memory['pending_calls'] ||= {} |
|
45 | 45 |
incoming_events.each do |event| |
46 |
- message = (event.payload[:message] || event.payload[:text] || event.payload[:sms]).to_s |
|
46 |
+ message = (event.payload['message'] || event.payload['text'] || event.payload['sms']).to_s |
|
47 | 47 |
if message != "" |
48 |
- if options[:receive_call].to_s == 'true' |
|
48 |
+ if options['receive_call'].to_s == 'true' |
|
49 | 49 |
secret = SecureRandom.hex 3 |
50 |
- memory[:pending_calls][secret] = message |
|
50 |
+ memory['pending_calls'][secret] = message |
|
51 | 51 |
make_call secret |
52 | 52 |
end |
53 |
- if options[:receive_text].to_s == 'true' |
|
53 |
+ if options['receive_text'].to_s == 'true' |
|
54 | 54 |
message = message.slice 0..160 |
55 | 55 |
send_message message |
56 | 56 |
end |
@@ -59,19 +59,19 @@ module Agents |
||
59 | 59 |
end |
60 | 60 |
|
61 | 61 |
def working? |
62 |
- last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago && !recent_error_logs? |
|
62 |
+ last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs? |
|
63 | 63 |
end |
64 | 64 |
|
65 | 65 |
def send_message(message) |
66 |
- @client.account.sms.messages.create :from => options[:sender_cell], |
|
67 |
- :to => options[:receiver_cell], |
|
66 |
+ @client.account.sms.messages.create :from => options['sender_cell'], |
|
67 |
+ :to => options['receiver_cell'], |
|
68 | 68 |
:body => message |
69 | 69 |
end |
70 | 70 |
|
71 | 71 |
def make_call(secret) |
72 |
- @client.account.calls.create :from => options[:sender_cell], |
|
73 |
- :to => options[:receiver_cell], |
|
74 |
- :url => post_url(options[:server_url],secret) |
|
72 |
+ @client.account.calls.create :from => options['sender_cell'], |
|
73 |
+ :to => options['receiver_cell'], |
|
74 |
+ :url => post_url(options['server_url'],secret) |
|
75 | 75 |
end |
76 | 76 |
|
77 | 77 |
def post_url(server_url,secret) |
@@ -79,9 +79,9 @@ module Agents |
||
79 | 79 |
end |
80 | 80 |
|
81 | 81 |
def receive_webhook(params) |
82 |
- if memory[:pending_calls].has_key? params[:secret] |
|
83 |
- response = Twilio::TwiML::Response.new {|r| r.Say memory[:pending_calls][params[:secret]], :voice => 'woman'} |
|
84 |
- memory[:pending_calls].delete params[:secret] |
|
82 |
+ if memory['pending_calls'].has_key? params['secret'] |
|
83 |
+ response = Twilio::TwiML::Response.new {|r| r.Say memory['pending_calls'][params['secret']], :voice => 'woman'} |
|
84 |
+ memory['pending_calls'].delete params['secret'] |
|
85 | 85 |
[response.text, 200] |
86 | 86 |
end |
87 | 87 |
end |
@@ -19,25 +19,25 @@ module Agents |
||
19 | 19 |
MD |
20 | 20 |
|
21 | 21 |
def validate_options |
22 |
- unless options[:username].present? && |
|
23 |
- options[:expected_update_period_in_days].present? |
|
22 |
+ unless options['username'].present? && |
|
23 |
+ options['expected_update_period_in_days'].present? |
|
24 | 24 |
errors.add(:base, "username and expected_update_period_in_days are required") |
25 | 25 |
end |
26 | 26 |
end |
27 | 27 |
|
28 | 28 |
def working? |
29 |
- (event = event_created_within(options[:expected_update_period_in_days])) && event.payload[:success] == true && !recent_error_logs? |
|
29 |
+ (event = event_created_within(options['expected_update_period_in_days'])) && event.payload['success'] == true && !recent_error_logs? |
|
30 | 30 |
end |
31 | 31 |
|
32 | 32 |
def default_options |
33 | 33 |
{ |
34 |
- :username => "", |
|
35 |
- :expected_update_period_in_days => "10", |
|
36 |
- :consumer_key => "---", |
|
37 |
- :consumer_secret => "---", |
|
38 |
- :oauth_token => "---", |
|
39 |
- :oauth_token_secret => "---", |
|
40 |
- :message_path => "text" |
|
34 |
+ 'username' => "", |
|
35 |
+ 'expected_update_period_in_days' => "10", |
|
36 |
+ 'consumer_key' => "---", |
|
37 |
+ 'consumer_secret' => "---", |
|
38 |
+ 'oauth_token' => "---", |
|
39 |
+ 'oauth_token_secret' => "---", |
|
40 |
+ 'message_path' => "text" |
|
41 | 41 |
} |
42 | 42 |
end |
43 | 43 |
|
@@ -47,22 +47,22 @@ module Agents |
||
47 | 47 |
incoming_events = incoming_events.first(20) |
48 | 48 |
end |
49 | 49 |
incoming_events.each do |event| |
50 |
- tweet_text = Utils.value_at(event.payload, options[:message_path]) |
|
50 |
+ tweet_text = Utils.value_at(event.payload, options['message_path']) |
|
51 | 51 |
begin |
52 | 52 |
publish_tweet tweet_text |
53 | 53 |
create_event :payload => { |
54 |
- :success => true, |
|
55 |
- :published_tweet => tweet_text, |
|
56 |
- :agent_id => event.agent_id, |
|
57 |
- :event_id => event.id |
|
54 |
+ 'success' => true, |
|
55 |
+ 'published_tweet' => tweet_text, |
|
56 |
+ 'agent_id' => event.agent_id, |
|
57 |
+ 'event_id' => event.id |
|
58 | 58 |
} |
59 | 59 |
rescue Twitter::Error => e |
60 | 60 |
create_event :payload => { |
61 |
- :success => false, |
|
62 |
- :error => e.message, |
|
63 |
- :failed_tweet => tweet_text, |
|
64 |
- :agent_id => event.agent_id, |
|
65 |
- :event_id => event.id |
|
61 |
+ 'success' => false, |
|
62 |
+ 'error' => e.message, |
|
63 |
+ 'failed_tweet' => tweet_text, |
|
64 |
+ 'agent_id' => event.agent_id, |
|
65 |
+ 'event_id' => event.id |
|
66 | 66 |
} |
67 | 67 |
end |
68 | 68 |
end |
@@ -54,26 +54,26 @@ module Agents |
||
54 | 54 |
default_schedule "11pm" |
55 | 55 |
|
56 | 56 |
def validate_options |
57 |
- unless options[:filters].present? && |
|
58 |
- options[:expected_update_period_in_days].present? && |
|
59 |
- options[:generate].present? |
|
57 |
+ unless options['filters'].present? && |
|
58 |
+ options['expected_update_period_in_days'].present? && |
|
59 |
+ options['generate'].present? |
|
60 | 60 |
errors.add(:base, "expected_update_period_in_days, generate, and filters are required fields") |
61 | 61 |
end |
62 | 62 |
end |
63 | 63 |
|
64 | 64 |
def working? |
65 |
- event_created_within(options[:expected_update_period_in_days]) && !recent_error_logs? |
|
65 |
+ event_created_within(options['expected_update_period_in_days']) && !recent_error_logs? |
|
66 | 66 |
end |
67 | 67 |
|
68 | 68 |
def default_options |
69 | 69 |
{ |
70 |
- :consumer_key => "---", |
|
71 |
- :consumer_secret => "---", |
|
72 |
- :oauth_token => "---", |
|
73 |
- :oauth_token_secret => "---", |
|
74 |
- :filters => %w[keyword1 keyword2], |
|
75 |
- :expected_update_period_in_days => "2", |
|
76 |
- :generate => "events" |
|
70 |
+ 'consumer_key' => "---", |
|
71 |
+ 'consumer_secret' => "---", |
|
72 |
+ 'oauth_token' => "---", |
|
73 |
+ 'oauth_token_secret' => "---", |
|
74 |
+ 'filters' => %w[keyword1 keyword2], |
|
75 |
+ 'expected_update_period_in_days' => "2", |
|
76 |
+ 'generate' => "events" |
|
77 | 77 |
} |
78 | 78 |
end |
79 | 79 |
|
@@ -81,33 +81,33 @@ module Agents |
||
81 | 81 |
filter = lookup_filter(filter) |
82 | 82 |
|
83 | 83 |
if filter |
84 |
- if options[:generate] == "counts" |
|
84 |
+ if options['generate'] == "counts" |
|
85 | 85 |
# Avoid memory pollution by reloading the Agent. |
86 | 86 |
agent = Agent.find(id) |
87 |
- agent.memory[:filter_counts] ||= {} |
|
88 |
- agent.memory[:filter_counts][filter] ||= 0 |
|
89 |
- agent.memory[:filter_counts][filter] += 1 |
|
90 |
- remove_unused_keys!(agent, :filter_counts) |
|
87 |
+ agent.memory['filter_counts'] ||= {} |
|
88 |
+ agent.memory['filter_counts'][filter] ||= 0 |
|
89 |
+ agent.memory['filter_counts'][filter] += 1 |
|
90 |
+ remove_unused_keys!(agent, 'filter_counts') |
|
91 | 91 |
agent.save! |
92 | 92 |
else |
93 |
- create_event :payload => status.merge(:filter => filter) |
|
93 |
+ create_event :payload => status.merge('filter' => filter) |
|
94 | 94 |
end |
95 | 95 |
end |
96 | 96 |
end |
97 | 97 |
|
98 | 98 |
def check |
99 |
- if options[:generate] == "counts" && memory[:filter_counts] && memory[:filter_counts].length > 0 |
|
100 |
- memory[:filter_counts].each do |filter, count| |
|
101 |
- create_event :payload => { :filter => filter, :count => count, :time => Time.now.to_i } |
|
99 |
+ if options['generate'] == "counts" && memory['filter_counts'] && memory['filter_counts'].length > 0 |
|
100 |
+ memory['filter_counts'].each do |filter, count| |
|
101 |
+ create_event :payload => { 'filter' => filter, 'count' => count, 'time' => Time.now.to_i } |
|
102 | 102 |
end |
103 | 103 |
end |
104 |
- memory[:filter_counts] = {} |
|
104 |
+ memory['filter_counts'] = {} |
|
105 | 105 |
end |
106 | 106 |
|
107 | 107 |
protected |
108 | 108 |
|
109 | 109 |
def lookup_filter(filter) |
110 |
- options[:filters].each do |known_filter| |
|
110 |
+ options['filters'].each do |known_filter| |
|
111 | 111 |
if known_filter == filter |
112 | 112 |
return filter |
113 | 113 |
elsif known_filter.is_a?(Array) |
@@ -120,7 +120,7 @@ module Agents |
||
120 | 120 |
|
121 | 121 |
def remove_unused_keys!(agent, base) |
122 | 122 |
if agent.memory[base] |
123 |
- (agent.memory[base].keys - agent.options[:filters].map {|f| f.is_a?(Array) ? f.first.to_s : f.to_s }).each do |removed_key| |
|
123 |
+ (agent.memory[base].keys - agent.options['filters'].map {|f| f.is_a?(Array) ? f.first.to_s : f.to_s }).each do |removed_key| |
|
124 | 124 |
agent.memory[base].delete(removed_key) |
125 | 125 |
end |
126 | 126 |
end |
@@ -41,36 +41,36 @@ module Agents |
||
41 | 41 |
default_schedule "every_1h" |
42 | 42 |
|
43 | 43 |
def validate_options |
44 |
- unless options[:username].present? && |
|
45 |
- options[:expected_update_period_in_days].present? |
|
44 |
+ unless options['username'].present? && |
|
45 |
+ options['expected_update_period_in_days'].present? |
|
46 | 46 |
errors.add(:base, "username and expected_update_period_in_days are required") |
47 | 47 |
end |
48 | 48 |
end |
49 | 49 |
|
50 | 50 |
def working? |
51 |
- event_created_within(options[:expected_update_period_in_days]) && !recent_error_logs? |
|
51 |
+ event_created_within(options['expected_update_period_in_days']) && !recent_error_logs? |
|
52 | 52 |
end |
53 | 53 |
|
54 | 54 |
def default_options |
55 | 55 |
{ |
56 |
- :username => "tectonic", |
|
57 |
- :expected_update_period_in_days => "2", |
|
58 |
- :consumer_key => "---", |
|
59 |
- :consumer_secret => "---", |
|
60 |
- :oauth_token => "---", |
|
61 |
- :oauth_token_secret => "---" |
|
56 |
+ 'username' => "tectonic", |
|
57 |
+ 'expected_update_period_in_days' => "2", |
|
58 |
+ 'consumer_key' => "---", |
|
59 |
+ 'consumer_secret' => "---", |
|
60 |
+ 'oauth_token' => "---", |
|
61 |
+ 'oauth_token_secret' => "---" |
|
62 | 62 |
} |
63 | 63 |
end |
64 | 64 |
|
65 | 65 |
def check |
66 |
- since_id = memory[:since_id] || nil |
|
66 |
+ since_id = memory['since_id'] || nil |
|
67 | 67 |
opts = {:count => 200, :include_rts => true, :exclude_replies => false, :include_entities => true, :contributor_details => true} |
68 | 68 |
opts.merge! :since_id => since_id unless since_id.nil? |
69 | 69 |
|
70 |
- tweets = Twitter.user_timeline(options[:username], opts) |
|
70 |
+ tweets = Twitter.user_timeline(options['username'], opts) |
|
71 | 71 |
|
72 | 72 |
tweets.each do |tweet| |
73 |
- memory[:since_id] = tweet.id if !memory[:since_id] || (tweet.id > memory[:since_id]) |
|
73 |
+ memory['since_id'] = tweet.id if !memory['since_id'] || (tweet.id > memory['since_id']) |
|
74 | 74 |
|
75 | 75 |
create_event :payload => tweet.attrs |
76 | 76 |
end |
@@ -34,11 +34,11 @@ module Agents |
||
34 | 34 |
end |
35 | 35 |
|
36 | 36 |
def default_options |
37 |
- { :secret => SecureRandom.hex(7) } |
|
37 |
+ { 'secret' => SecureRandom.hex(7) } |
|
38 | 38 |
end |
39 | 39 |
|
40 | 40 |
def validate_options |
41 |
- errors.add(:base, "secret is required and must be longer than 4 characters") unless options[:secret].present? && options[:secret].length > 4 |
|
41 |
+ errors.add(:base, "secret is required and must be longer than 4 characters") unless options['secret'].present? && options['secret'].length > 4 |
|
42 | 42 |
end |
43 | 43 |
end |
44 | 44 |
end |
@@ -45,30 +45,30 @@ module Agents |
||
45 | 45 |
end |
46 | 46 |
|
47 | 47 |
def wunderground |
48 |
- Wunderground.new(options[:api_key]) if key_setup? |
|
48 |
+ Wunderground.new(options['api_key']) if key_setup? |
|
49 | 49 |
end |
50 | 50 |
|
51 | 51 |
def key_setup? |
52 |
- options[:api_key] && options[:api_key] != "your-key" |
|
52 |
+ options['api_key'] && options['api_key'] != "your-key" |
|
53 | 53 |
end |
54 | 54 |
|
55 | 55 |
def default_options |
56 | 56 |
{ |
57 |
- :api_key => "your-key", |
|
58 |
- :location => "94103" |
|
57 |
+ 'api_key' => "your-key", |
|
58 |
+ 'location' => "94103" |
|
59 | 59 |
} |
60 | 60 |
end |
61 | 61 |
|
62 | 62 |
def validate_options |
63 |
- errors.add(:base, "location is required") unless options[:location].present? || options[:zipcode].present? |
|
64 |
- errors.add(:base, "api_key is required") unless options[:api_key].present? |
|
63 |
+ errors.add(:base, "location is required") unless options['location'].present? || options['zipcode'].present? |
|
64 |
+ errors.add(:base, "api_key is required") unless options['api_key'].present? |
|
65 | 65 |
end |
66 | 66 |
|
67 | 67 |
def check |
68 | 68 |
if key_setup? |
69 |
- wunderground.forecast_for(options[:location] || options[:zipcode])["forecast"]["simpleforecast"]["forecastday"].each do |day| |
|
69 |
+ wunderground.forecast_for(options['location'] || options['zipcode'])["forecast"]["simpleforecast"]["forecastday"].each do |day| |
|
70 | 70 |
if is_tomorrow?(day) |
71 |
- create_event :payload => day.merge(:location => options[:location] || options[:zipcode]) |
|
71 |
+ create_event :payload => day.merge('location' => options['location'] || options['zipcode']) |
|
72 | 72 |
end |
73 | 73 |
end |
74 | 74 |
end |
@@ -15,19 +15,19 @@ module Agents |
||
15 | 15 |
|
16 | 16 |
To tell the Agent how to parse the content, specify `extract` as a hash with keys naming the extractions and values of hashes. |
17 | 17 |
|
18 |
- When parsing HTML or XML, these sub-hashes specify how to extract with a `:css` CSS selector and either `:text => true` or `attr` pointing to an attribute name to grab. An example: |
|
18 |
+ When parsing HTML or XML, these sub-hashes specify how to extract with a `css` CSS selector and either `'text': true` or `attr` pointing to an attribute name to grab. An example: |
|
19 | 19 |
|
20 |
- :extract => { |
|
21 |
- :url => { :css => "#comic img", :attr => "src" }, |
|
22 |
- :title => { :css => "#comic img", :attr => "title" }, |
|
23 |
- :body_text => { :css => "div.main", :text => true } |
|
20 |
+ 'extract': { |
|
21 |
+ 'url': { 'css': "#comic img", 'attr': "src" }, |
|
22 |
+ 'title': { 'css': "#comic img", 'attr': "title" }, |
|
23 |
+ 'body_text': { 'css': "div.main", 'text': true } |
|
24 | 24 |
} |
25 | 25 |
|
26 | 26 |
When parsing JSON, these sub-hashes specify [JSONPaths](http://goessner.net/articles/JsonPath/) to the values that you care about. For example: |
27 | 27 |
|
28 |
- :extract => { |
|
29 |
- :title => { :path => "results.data[*].title" }, |
|
30 |
- :description => { :path => "results.data[*].description" } |
|
28 |
+ 'extract': { |
|
29 |
+ 'title': { 'path': "results.data[*].title" }, |
|
30 |
+ 'description': { 'path': "results.data[*].description" } |
|
31 | 31 |
} |
32 | 32 |
|
33 | 33 |
Note that for all of the formats, whatever you extract MUST have the same number of matches for each extractor. E.g., if you're extracting rows, all extractors must match all rows. For generating CSS selectors, something like [SelectorGadget](http://selectorgadget.com) may be helpful. |
@@ -36,7 +36,7 @@ module Agents |
||
36 | 36 |
MD |
37 | 37 |
|
38 | 38 |
event_description do |
39 |
- "Events will have the fields you specified. Your options look like:\n\n #{Utils.pretty_print options[:extract]}" |
|
39 |
+ "Events will have the fields you specified. Your options look like:\n\n #{Utils.pretty_print options['extract']}" |
|
40 | 40 |
end |
41 | 41 |
|
42 | 42 |
default_schedule "every_12h" |
@@ -44,33 +44,33 @@ module Agents |
||
44 | 44 |
UNIQUENESS_LOOK_BACK = 30 |
45 | 45 |
|
46 | 46 |
def working? |
47 |
- event_created_within(options[:expected_update_period_in_days]) && !recent_error_logs? |
|
47 |
+ event_created_within(options['expected_update_period_in_days']) && !recent_error_logs? |
|
48 | 48 |
end |
49 | 49 |
|
50 | 50 |
def default_options |
51 | 51 |
{ |
52 |
- :expected_update_period_in_days => "2", |
|
53 |
- :url => "http://xkcd.com", |
|
54 |
- :type => "html", |
|
55 |
- :mode => :on_change, |
|
56 |
- :extract => { |
|
57 |
- :url => {:css => "#comic img", :attr => "src"}, |
|
58 |
- :title => {:css => "#comic img", :attr => "title"} |
|
52 |
+ 'expected_update_period_in_days' => "2", |
|
53 |
+ 'url' => "http://xkcd.com", |
|
54 |
+ 'type' => "html", |
|
55 |
+ 'mode' => :on_change, |
|
56 |
+ 'extract' => { |
|
57 |
+ 'url' => {'css' => "#comic img", 'attr' => "src"}, |
|
58 |
+ 'title' => {'css' => "#comic img", 'attr' => "title"} |
|
59 | 59 |
} |
60 | 60 |
} |
61 | 61 |
end |
62 | 62 |
|
63 | 63 |
def validate_options |
64 |
- errors.add(:base, "url and expected_update_period_in_days are required") unless options[:expected_update_period_in_days].present? && options[:url].present? |
|
65 |
- if !options[:extract].present? && extraction_type != "json" |
|
64 |
+ errors.add(:base, "url and expected_update_period_in_days are required") unless options['expected_update_period_in_days'].present? && options['url'].present? |
|
65 |
+ if !options['extract'].present? && extraction_type != "json" |
|
66 | 66 |
errors.add(:base, "extract is required for all types except json") |
67 | 67 |
end |
68 | 68 |
end |
69 | 69 |
|
70 | 70 |
def check |
71 | 71 |
hydra = Typhoeus::Hydra.new |
72 |
- log "Fetching #{options[:url]}" |
|
73 |
- request = Typhoeus::Request.new(options[:url], :followlocation => true) |
|
72 |
+ log "Fetching #{options['url']}" |
|
73 |
+ request = Typhoeus::Request.new(options['url'], :followlocation => true) |
|
74 | 74 |
request.on_failure do |response| |
75 | 75 |
error "Failed: #{response.inspect}" |
76 | 76 |
end |
@@ -85,37 +85,37 @@ module Agents |
||
85 | 85 |
end |
86 | 86 |
else |
87 | 87 |
output = {} |
88 |
- options[:extract].each do |name, extraction_details| |
|
88 |
+ options['extract'].each do |name, extraction_details| |
|
89 | 89 |
result = if extraction_type == "json" |
90 |
- output[name] = Utils.values_at(doc, extraction_details[:path]) |
|
90 |
+ output[name] = Utils.values_at(doc, extraction_details['path']) |
|
91 | 91 |
else |
92 |
- output[name] = doc.css(extraction_details[:css]).map { |node| |
|
93 |
- if extraction_details[:attr] |
|
94 |
- node.attr(extraction_details[:attr]) |
|
95 |
- elsif extraction_details[:text] |
|
92 |
+ output[name] = doc.css(extraction_details['css']).map { |node| |
|
93 |
+ if extraction_details['attr'] |
|
94 |
+ node.attr(extraction_details['attr']) |
|
95 |
+ elsif extraction_details['text'] |
|
96 | 96 |
node.text() |
97 | 97 |
else |
98 |
- error ":attr or :text is required on HTML or XML extraction patterns" |
|
98 |
+ error "'attr' or 'text' is required on HTML or XML extraction patterns" |
|
99 | 99 |
return |
100 | 100 |
end |
101 | 101 |
} |
102 | 102 |
end |
103 |
- log "Extracting #{extraction_type} at #{extraction_details[:path] || extraction_details[:css]}: #{result}" |
|
103 |
+ log "Extracting #{extraction_type} at #{extraction_details['path'] || extraction_details['css']}: #{result}" |
|
104 | 104 |
end |
105 | 105 |
|
106 |
- num_unique_lengths = options[:extract].keys.map { |name| output[name].length }.uniq |
|
106 |
+ num_unique_lengths = options['extract'].keys.map { |name| output[name].length }.uniq |
|
107 | 107 |
|
108 | 108 |
if num_unique_lengths.length != 1 |
109 |
- error "Got an uneven number of matches for #{options[:name]}: #{options[:extract].inspect}" |
|
109 |
+ error "Got an uneven number of matches for #{options['name']}: #{options['extract'].inspect}" |
|
110 | 110 |
return |
111 | 111 |
end |
112 | 112 |
|
113 | 113 |
num_unique_lengths.first.times do |index| |
114 | 114 |
result = {} |
115 |
- options[:extract].keys.each do |name| |
|
115 |
+ options['extract'].keys.each do |name| |
|
116 | 116 |
result[name] = output[name][index] |
117 | 117 |
if name.to_s == 'url' |
118 |
- result[name] = URI.join(options[:url], result[name]).to_s if (result[name] =~ URI::DEFAULT_PARSER.regexp[:ABS_URI]).nil? |
|
118 |
+ result[name] = URI.join(options['url'], result[name]).to_s if (result[name] =~ URI::DEFAULT_PARSER.regexp[:ABS_URI]).nil? |
|
119 | 119 |
end |
120 | 120 |
end |
121 | 121 |
|
@@ -133,22 +133,22 @@ module Agents |
||
133 | 133 |
private |
134 | 134 |
|
135 | 135 |
def store_payload? result |
136 |
- !options[:mode] || options[:mode].to_s == "all" || (options[:mode].to_s == "on_change" && !previous_payloads.include?(result.to_json)) |
|
136 |
+ !options['mode'] || options['mode'].to_s == "all" || (options['mode'].to_s == "on_change" && !previous_payloads.include?(result.to_json)) |
|
137 | 137 |
end |
138 | 138 |
|
139 | 139 |
def previous_payloads |
140 |
- events.order("id desc").limit(UNIQUENESS_LOOK_BACK).pluck(:payload).map(&:to_json) if options[:mode].to_s == "on_change" |
|
140 |
+ events.order("id desc").limit(UNIQUENESS_LOOK_BACK).pluck(:payload).map(&:to_json) if options['mode'].to_s == "on_change" |
|
141 | 141 |
end |
142 | 142 |
|
143 | 143 |
def extract_full_json? |
144 |
- (!options[:extract].present? && extraction_type == "json") |
|
144 |
+ (!options['extract'].present? && extraction_type == "json") |
|
145 | 145 |
end |
146 | 146 |
|
147 | 147 |
def extraction_type |
148 |
- (options[:type] || begin |
|
149 |
- if options[:url] =~ /\.(rss|xml)$/i |
|
148 |
+ (options['type'] || begin |
|
149 |
+ if options['url'] =~ /\.(rss|xml)$/i |
|
150 | 150 |
"xml" |
151 |
- elsif options[:url] =~ /\.json$/i |
|
151 |
+ elsif options['url'] =~ /\.json$/i |
|
152 | 152 |
"json" |
153 | 153 |
else |
154 | 154 |
"html" |
@@ -20,24 +20,24 @@ module Agents |
||
20 | 20 |
MD |
21 | 21 |
|
22 | 22 |
def validate_options |
23 |
- unless options[:uid].present? && |
|
24 |
- options[:expected_update_period_in_days].present? |
|
23 |
+ unless options['uid'].present? && |
|
24 |
+ options['expected_update_period_in_days'].present? |
|
25 | 25 |
errors.add(:base, "expected_update_period_in_days and uid are required") |
26 | 26 |
end |
27 | 27 |
end |
28 | 28 |
|
29 | 29 |
def working? |
30 |
- (event = event_created_within(options[:expected_update_period_in_days])) && event.payload[:success] == true && !recent_error_logs? |
|
30 |
+ (event = event_created_within(options['expected_update_period_in_days'])) && event.payload['success'] == true && !recent_error_logs? |
|
31 | 31 |
end |
32 | 32 |
|
33 | 33 |
def default_options |
34 | 34 |
{ |
35 |
- :uid => "", |
|
36 |
- :access_token => "---", |
|
37 |
- :app_key => "---", |
|
38 |
- :app_secret => "---", |
|
39 |
- :expected_update_period_in_days => "10", |
|
40 |
- :message_path => "text" |
|
35 |
+ 'uid' => "", |
|
36 |
+ 'access_token' => "---", |
|
37 |
+ 'app_key' => "---", |
|
38 |
+ 'app_secret' => "---", |
|
39 |
+ 'expected_update_period_in_days' => "10", |
|
40 |
+ 'message_path' => "text" |
|
41 | 41 |
} |
42 | 42 |
end |
43 | 43 |
|
@@ -47,25 +47,25 @@ module Agents |
||
47 | 47 |
incoming_events = incoming_events.first(20) |
48 | 48 |
end |
49 | 49 |
incoming_events.each do |event| |
50 |
- tweet_text = Utils.value_at(event.payload, options[:message_path]) |
|
50 |
+ tweet_text = Utils.value_at(event.payload, options['message_path']) |
|
51 | 51 |
if event.agent.type == "Agents::TwitterUserAgent" |
52 | 52 |
tweet_text = unwrap_tco_urls(tweet_text, event.payload) |
53 | 53 |
end |
54 | 54 |
begin |
55 | 55 |
publish_tweet tweet_text |
56 | 56 |
create_event :payload => { |
57 |
- :success => true, |
|
58 |
- :published_tweet => tweet_text, |
|
59 |
- :agent_id => event.agent_id, |
|
60 |
- :event_id => event.id |
|
57 |
+ 'success' => true, |
|
58 |
+ 'published_tweet' => tweet_text, |
|
59 |
+ 'agent_id' => event.agent_id, |
|
60 |
+ 'event_id' => event.id |
|
61 | 61 |
} |
62 | 62 |
rescue OAuth2::Error => e |
63 | 63 |
create_event :payload => { |
64 |
- :success => false, |
|
65 |
- :error => e.message, |
|
66 |
- :failed_tweet => tweet_text, |
|
67 |
- :agent_id => event.agent_id, |
|
68 |
- :event_id => event.id |
|
64 |
+ 'success' => false, |
|
65 |
+ 'error' => e.message, |
|
66 |
+ 'failed_tweet' => tweet_text, |
|
67 |
+ 'agent_id' => event.agent_id, |
|
68 |
+ 'event_id' => event.id |
|
69 | 69 |
} |
70 | 70 |
end |
71 | 71 |
end |
@@ -70,29 +70,29 @@ module Agents |
||
70 | 70 |
default_schedule "every_1h" |
71 | 71 |
|
72 | 72 |
def validate_options |
73 |
- unless options[:uid].present? && |
|
74 |
- options[:expected_update_period_in_days].present? |
|
73 |
+ unless options['uid'].present? && |
|
74 |
+ options['expected_update_period_in_days'].present? |
|
75 | 75 |
errors.add(:base, "expected_update_period_in_days and uid are required") |
76 | 76 |
end |
77 | 77 |
end |
78 | 78 |
|
79 | 79 |
def working? |
80 |
- event_created_within(options[:expected_update_period_in_days]) && !recent_error_logs? |
|
80 |
+ event_created_within(options['expected_update_period_in_days']) && !recent_error_logs? |
|
81 | 81 |
end |
82 | 82 |
|
83 | 83 |
def default_options |
84 | 84 |
{ |
85 |
- :uid => "", |
|
86 |
- :access_token => "---", |
|
87 |
- :app_key => "---", |
|
88 |
- :app_secret => "---", |
|
89 |
- :expected_update_period_in_days => "2" |
|
85 |
+ 'uid' => "", |
|
86 |
+ 'access_token' => "---", |
|
87 |
+ 'app_key' => "---", |
|
88 |
+ 'app_secret' => "---", |
|
89 |
+ 'expected_update_period_in_days' => "2" |
|
90 | 90 |
} |
91 | 91 |
end |
92 | 92 |
|
93 | 93 |
def check |
94 |
- since_id = memory[:since_id] || nil |
|
95 |
- opts = {:uid => options[:uid].to_i} |
|
94 |
+ since_id = memory['since_id'] || nil |
|
95 |
+ opts = {:uid => options['uid'].to_i} |
|
96 | 96 |
opts.merge! :since_id => since_id unless since_id.nil? |
97 | 97 |
|
98 | 98 |
# http://open.weibo.com/wiki/2/statuses/user_timeline/en |
@@ -101,7 +101,7 @@ module Agents |
||
101 | 101 |
|
102 | 102 |
|
103 | 103 |
resp[:statuses].each do |status| |
104 |
- memory[:since_id] = status.id if !memory[:since_id] || (status.id > memory[:since_id]) |
|
104 |
+ memory['since_id'] = status.id if !memory['since_id'] || (status.id > memory['since_id']) |
|
105 | 105 |
|
106 | 106 |
create_event :payload => status.as_json |
107 | 107 |
end |
@@ -1,13 +1,16 @@ |
||
1 |
-require 'serialize_and_normalize' |
|
1 |
+require 'json_with_indifferent_access' |
|
2 | 2 |
|
3 | 3 |
class Event < ActiveRecord::Base |
4 |
- include SerializeAndNormalize |
|
5 |
- |
|
6 | 4 |
attr_accessible :lat, :lng, :payload, :user_id, :user, :expires_at |
7 | 5 |
|
8 | 6 |
acts_as_mappable |
9 | 7 |
|
10 |
- serialize_and_normalize :payload |
|
8 |
+ serialize :payload, JSONWithIndifferentAccess |
|
9 |
+ |
|
10 |
+ def payload=(o) |
|
11 |
+ self[:payload] = ActiveSupport::HashWithIndifferentAccess.new(o) |
|
12 |
+ end |
|
13 |
+ |
|
11 | 14 |
|
12 | 15 |
belongs_to :user |
13 | 16 |
belongs_to :agent, :counter_cache => true |
@@ -0,0 +1,9 @@ |
||
1 |
+class JSONWithIndifferentAccess |
|
2 |
+ def self.load(json) |
|
3 |
+ ActiveSupport::HashWithIndifferentAccess.new(JSON.load(json || '{}')) |
|
4 |
+ end |
|
5 |
+ |
|
6 |
+ def self.dump(hash) |
|
7 |
+ JSON.dump(hash) |
|
8 |
+ end |
|
9 |
+end |
@@ -1,46 +0,0 @@ |
||
1 |
-module SerializeAndNormalize |
|
2 |
- extend ActiveSupport::Concern |
|
3 |
- |
|
4 |
- module ClassMethods |
|
5 |
- def serialize_and_normalize(*column_names) |
|
6 |
- column_names.flatten.uniq.compact.map(&:to_sym).each do |column_name| |
|
7 |
- setup_name = "setup_#{column_name}".to_sym |
|
8 |
- normalize_name = "normalize_#{column_name}".to_sym |
|
9 |
- validate_name = "validate_#{column_name}".to_sym |
|
10 |
- |
|
11 |
- serialize column_name, JSON |
|
12 |
- after_initialize setup_name |
|
13 |
- before_validation normalize_name |
|
14 |
- before_save normalize_name |
|
15 |
- validate validate_name |
|
16 |
- |
|
17 |
- class_eval <<-RUBY |
|
18 |
- def #{setup_name} |
|
19 |
- self[:#{column_name}] ||= ActiveSupport::HashWithIndifferentAccess.new |
|
20 |
- end |
|
21 |
- |
|
22 |
- def #{validate_name} |
|
23 |
- # Implement me in your subclass. |
|
24 |
- end |
|
25 |
- |
|
26 |
- def #{normalize_name} |
|
27 |
- self.#{column_name} = self[:#{column_name}] |
|
28 |
- end |
|
29 |
- |
|
30 |
- def #{column_name}=(data) |
|
31 |
- data = (JSON.parse(data) rescue data) if data.is_a?(String) |
|
32 |
- |
|
33 |
- case data |
|
34 |
- when ActiveSupport::HashWithIndifferentAccess |
|
35 |
- self[:#{column_name}] = data |
|
36 |
- when Hash |
|
37 |
- self[:#{column_name}] = ActiveSupport::HashWithIndifferentAccess.new(data) |
|
38 |
- else |
|
39 |
- self[:#{column_name}] = data |
|
40 |
- end |
|
41 |
- end |
|
42 |
- RUBY |
|
43 |
- end |
|
44 |
- end |
|
45 |
- end |
|
46 |
-end |
@@ -5,7 +5,7 @@ describe AgentsController do |
||
5 | 5 |
{ |
6 | 6 |
:type => "Agents::WebsiteAgent", |
7 | 7 |
:name => "Something", |
8 |
- :options => agents(:bob_website_agent).options.to_json, |
|
8 |
+ :options => agents(:bob_website_agent).options, |
|
9 | 9 |
:source_ids => [agents(:bob_weather_agent).id, ""] |
10 | 10 |
}.merge(options) |
11 | 11 |
end |
@@ -11,7 +11,7 @@ jane_website_agent: |
||
11 | 11 |
:title => {:css => "item title", :text => true}, |
12 | 12 |
:url => {:css => "item link", :text => true} |
13 | 13 |
} |
14 |
- }.to_yaml.inspect %> |
|
14 |
+ }.to_json.inspect %> |
|
15 | 15 |
|
16 | 16 |
bob_website_agent: |
17 | 17 |
type: Agents::WebsiteAgent |
@@ -26,7 +26,7 @@ bob_website_agent: |
||
26 | 26 |
:url => {:css => "#comic img", :attr => "src"}, |
27 | 27 |
:title => {:css => "#comic img", :attr => "title"} |
28 | 28 |
} |
29 |
- }.to_yaml.inspect %> |
|
29 |
+ }.to_json.inspect %> |
|
30 | 30 |
|
31 | 31 |
bob_weather_agent: |
32 | 32 |
type: Agents::WeatherAgent |
@@ -38,7 +38,7 @@ bob_weather_agent: |
||
38 | 38 |
:lat => 37.779329, |
39 | 39 |
:lng => -122.41915, |
40 | 40 |
:api_key => 'test' |
41 |
- }.to_yaml.inspect %> |
|
41 |
+ }.to_json.inspect %> |
|
42 | 42 |
|
43 | 43 |
jane_weather_agent: |
44 | 44 |
type: Agents::WeatherAgent |
@@ -50,7 +50,7 @@ jane_weather_agent: |
||
50 | 50 |
:lat => 37.779329, |
51 | 51 |
:lng => -122.41915, |
52 | 52 |
:api_key => 'test' |
53 |
- }.to_yaml.inspect %> |
|
53 |
+ }.to_json.inspect %> |
|
54 | 54 |
|
55 | 55 |
jane_rain_notifier_agent: |
56 | 56 |
type: Agents::TriggerAgent |
@@ -64,7 +64,7 @@ jane_rain_notifier_agent: |
||
64 | 64 |
:path => "conditions" |
65 | 65 |
}], |
66 | 66 |
:message => "Just so you know, it looks like '<conditions>' tomorrow in <location>" |
67 |
- }.to_yaml.inspect %> |
|
67 |
+ }.to_json.inspect %> |
|
68 | 68 |
|
69 | 69 |
bob_rain_notifier_agent: |
70 | 70 |
type: Agents::TriggerAgent |
@@ -78,7 +78,7 @@ bob_rain_notifier_agent: |
||
78 | 78 |
:path => "conditions" |
79 | 79 |
}], |
80 | 80 |
:message => "Just so you know, it looks like '<conditions>' tomorrow in <location>" |
81 |
- }.to_yaml.inspect %> |
|
81 |
+ }.to_json.inspect %> |
|
82 | 82 |
|
83 | 83 |
bob_twitter_user_agent: |
84 | 84 |
type: Agents::TwitterUserAgent |
@@ -91,7 +91,7 @@ bob_twitter_user_agent: |
||
91 | 91 |
:consumer_secret => "---", |
92 | 92 |
:oauth_token => "---", |
93 | 93 |
:oauth_token_secret => "---" |
94 |
- }.to_yaml.inspect %> |
|
94 |
+ }.to_json.inspect %> |
|
95 | 95 |
|
96 | 96 |
bob_manual_event_agent: |
97 | 97 |
type: Agents::ManualEventAgent |
@@ -1,9 +1,9 @@ |
||
1 | 1 |
bob_website_agent_event: |
2 | 2 |
user: bob |
3 | 3 |
agent: bob_website_agent |
4 |
- payload: <%= [{ :title => "foo", :url => "http://foo.com" }].to_yaml.inspect %> |
|
4 |
+ payload: <%= [{ :title => "foo", :url => "http://foo.com" }].to_json.inspect %> |
|
5 | 5 |
|
6 | 6 |
jane_website_agent_event: |
7 | 7 |
user: jane |
8 | 8 |
agent: jane_website_agent |
9 |
- payload: <%= [{ :title => "foo", :url => "http://foo.com" }].to_yaml.inspect %> |
|
9 |
+ payload: <%= [{ :title => "foo", :url => "http://foo.com" }].to_json.inspect %> |
@@ -261,9 +261,9 @@ describe Agent do |
||
261 | 261 |
it "symbolizes memory before validating" do |
262 | 262 |
agent = Agents::SomethingSource.new(:name => "something") |
263 | 263 |
agent.user = users(:bob) |
264 |
- agent.memory["bad"] = :hello |
|
264 |
+ agent.memory["bad"] = 2 |
|
265 | 265 |
agent.save |
266 |
- agent.memory[:bad].should == :hello |
|
266 |
+ agent.memory[:bad].should == 2 |
|
267 | 267 |
end |
268 | 268 |
|
269 | 269 |
it "should not allow agents owned by other people" do |
@@ -19,16 +19,16 @@ describe Agents::DigestEmailAgent do |
||
19 | 19 |
it "queues any payloads it receives" do |
20 | 20 |
event1 = Event.new |
21 | 21 |
event1.agent = agents(:bob_rain_notifier_agent) |
22 |
- event1.payload = "Something you should know about" |
|
22 |
+ event1.payload = { :data => "Something you should know about" } |
|
23 | 23 |
event1.save! |
24 | 24 |
|
25 | 25 |
event2 = Event.new |
26 | 26 |
event2.agent = agents(:bob_weather_agent) |
27 |
- event2.payload = "Something else you should know about" |
|
27 |
+ event2.payload = { :data => "Something else you should know about" } |
|
28 | 28 |
event2.save! |
29 | 29 |
|
30 | 30 |
Agents::DigestEmailAgent.async_receive(@checker.id, [event1.id, event2.id]) |
31 |
- @checker.reload.memory[:queue].should == ["Something you should know about", "Something else you should know about"] |
|
31 |
+ @checker.reload.memory[:queue].should == [{ 'data' => "Something you should know about" }, { 'data' => "Something else you should know about" }] |
|
32 | 32 |
end |
33 | 33 |
end |
34 | 34 |
|
@@ -37,7 +37,7 @@ describe Agents::DigestEmailAgent do |
||
37 | 37 |
Agents::DigestEmailAgent.async_check(@checker.id) |
38 | 38 |
ActionMailer::Base.deliveries.should == [] |
39 | 39 |
|
40 |
- @checker.memory[:queue] = ["Something you should know about", |
|
40 |
+ @checker.memory[:queue] = [{ :data => "Something you should know about" }, |
|
41 | 41 |
{ :title => "Foo", :url => "http://google.com", :bar => 2 }, |
42 | 42 |
{ "message" => "hi", :woah => "there" }, |
43 | 43 |
{ "test" => 2 }] |
@@ -47,7 +47,7 @@ describe Agents::DigestEmailAgent do |
||
47 | 47 |
Agents::DigestEmailAgent.async_check(@checker.id) |
48 | 48 |
ActionMailer::Base.deliveries.last.to.should == ["bob@example.com"] |
49 | 49 |
ActionMailer::Base.deliveries.last.subject.should == "something interesting" |
50 |
- get_message_part(ActionMailer::Base.deliveries.last, /plain/).strip.should == "Something you should know about\n\nFoo\n bar: 2\n url: http://google.com\n\nhi\n woah: there\n\nEvent\n test: 2" |
|
50 |
+ get_message_part(ActionMailer::Base.deliveries.last, /plain/).strip.should == "Event\n data: Something you should know about\n\nFoo\n bar: 2\n url: http://google.com\n\nhi\n woah: there\n\nEvent\n test: 2" |
|
51 | 51 |
@checker.reload.memory[:queue].should be_empty |
52 | 52 |
end |
53 | 53 |
|
@@ -21,12 +21,12 @@ describe Agents::EmailAgent do |
||
21 | 21 |
|
22 | 22 |
event1 = Event.new |
23 | 23 |
event1.agent = agents(:bob_rain_notifier_agent) |
24 |
- event1.payload = "Something you should know about" |
|
24 |
+ event1.payload = { :data => "Something you should know about" } |
|
25 | 25 |
event1.save! |
26 | 26 |
|
27 | 27 |
event2 = Event.new |
28 | 28 |
event2.agent = agents(:bob_weather_agent) |
29 |
- event2.payload = "Something else you should know about" |
|
29 |
+ event2.payload = { :data => "Something else you should know about" } |
|
30 | 30 |
event2.save! |
31 | 31 |
|
32 | 32 |
Agents::EmailAgent.async_receive(@checker.id, [event1.id]) |
@@ -35,8 +35,8 @@ describe Agents::EmailAgent do |
||
35 | 35 |
ActionMailer::Base.deliveries.count.should == 2 |
36 | 36 |
ActionMailer::Base.deliveries.last.to.should == ["bob@example.com"] |
37 | 37 |
ActionMailer::Base.deliveries.last.subject.should == "something interesting" |
38 |
- get_message_part(ActionMailer::Base.deliveries.last, /plain/).strip.should == "Something else you should know about" |
|
39 |
- get_message_part(ActionMailer::Base.deliveries.first, /plain/).strip.should == "Something you should know about" |
|
38 |
+ get_message_part(ActionMailer::Base.deliveries.last, /plain/).strip.should == "Event\n data: Something else you should know about" |
|
39 |
+ get_message_part(ActionMailer::Base.deliveries.first, /plain/).strip.should == "Event\n data: Something you should know about" |
|
40 | 40 |
end |
41 | 41 |
|
42 | 42 |
it "can receive complex events and send them on" do |
@@ -9,8 +9,8 @@ describe Agents::HumanTaskAgent do |
||
9 | 9 |
|
10 | 10 |
@event = Event.new |
11 | 11 |
@event.agent = agents(:bob_rain_notifier_agent) |
12 |
- @event.payload = { :foo => { "bar" => { :baz => "a2b" } }, |
|
13 |
- :name => "Joe" } |
|
12 |
+ @event.payload = { 'foo' => { "bar" => { 'baz' => "a2b" } }, |
|
13 |
+ 'name' => "Joe" } |
|
14 | 14 |
@event.id = 345 |
15 | 15 |
|
16 | 16 |
@checker.should be_valid |
@@ -18,110 +18,110 @@ describe Agents::HumanTaskAgent do |
||
18 | 18 |
|
19 | 19 |
describe "validations" do |
20 | 20 |
it "validates that trigger_on is 'schedule' or 'event'" do |
21 |
- @checker.options[:trigger_on] = "foo" |
|
21 |
+ @checker.options['trigger_on'] = "foo" |
|
22 | 22 |
@checker.should_not be_valid |
23 | 23 |
end |
24 | 24 |
|
25 | 25 |
it "requires expected_receive_period_in_days when trigger_on is set to 'event'" do |
26 |
- @checker.options[:trigger_on] = "event" |
|
27 |
- @checker.options[:expected_receive_period_in_days] = nil |
|
26 |
+ @checker.options['trigger_on'] = "event" |
|
27 |
+ @checker.options['expected_receive_period_in_days'] = nil |
|
28 | 28 |
@checker.should_not be_valid |
29 |
- @checker.options[:expected_receive_period_in_days] = 2 |
|
29 |
+ @checker.options['expected_receive_period_in_days'] = 2 |
|
30 | 30 |
@checker.should be_valid |
31 | 31 |
end |
32 | 32 |
|
33 | 33 |
it "requires a positive submission_period when trigger_on is set to 'schedule'" do |
34 |
- @checker.options[:trigger_on] = "schedule" |
|
35 |
- @checker.options[:submission_period] = nil |
|
34 |
+ @checker.options['trigger_on'] = "schedule" |
|
35 |
+ @checker.options['submission_period'] = nil |
|
36 | 36 |
@checker.should_not be_valid |
37 |
- @checker.options[:submission_period] = 2 |
|
37 |
+ @checker.options['submission_period'] = 2 |
|
38 | 38 |
@checker.should be_valid |
39 | 39 |
end |
40 | 40 |
|
41 | 41 |
it "requires a hit.title" do |
42 |
- @checker.options[:hit][:title] = "" |
|
42 |
+ @checker.options['hit']['title'] = "" |
|
43 | 43 |
@checker.should_not be_valid |
44 | 44 |
end |
45 | 45 |
|
46 | 46 |
it "requires a hit.description" do |
47 |
- @checker.options[:hit][:description] = "" |
|
47 |
+ @checker.options['hit']['description'] = "" |
|
48 | 48 |
@checker.should_not be_valid |
49 | 49 |
end |
50 | 50 |
|
51 | 51 |
it "requires hit.assignments" do |
52 |
- @checker.options[:hit][:assignments] = "" |
|
52 |
+ @checker.options['hit']['assignments'] = "" |
|
53 | 53 |
@checker.should_not be_valid |
54 |
- @checker.options[:hit][:assignments] = 0 |
|
54 |
+ @checker.options['hit']['assignments'] = 0 |
|
55 | 55 |
@checker.should_not be_valid |
56 |
- @checker.options[:hit][:assignments] = "moose" |
|
56 |
+ @checker.options['hit']['assignments'] = "moose" |
|
57 | 57 |
@checker.should_not be_valid |
58 |
- @checker.options[:hit][:assignments] = "2" |
|
58 |
+ @checker.options['hit']['assignments'] = "2" |
|
59 | 59 |
@checker.should be_valid |
60 | 60 |
end |
61 | 61 |
|
62 | 62 |
it "requires hit.questions" do |
63 |
- old_questions = @checker.options[:hit][:questions] |
|
64 |
- @checker.options[:hit][:questions] = nil |
|
63 |
+ old_questions = @checker.options['hit']['questions'] |
|
64 |
+ @checker.options['hit']['questions'] = nil |
|
65 | 65 |
@checker.should_not be_valid |
66 |
- @checker.options[:hit][:questions] = [] |
|
66 |
+ @checker.options['hit']['questions'] = [] |
|
67 | 67 |
@checker.should_not be_valid |
68 |
- @checker.options[:hit][:questions] = [old_questions[0]] |
|
68 |
+ @checker.options['hit']['questions'] = [old_questions[0]] |
|
69 | 69 |
@checker.should be_valid |
70 | 70 |
end |
71 | 71 |
|
72 | 72 |
it "requires that all questions have key, name, required, type, and question" do |
73 |
- old_questions = @checker.options[:hit][:questions] |
|
74 |
- @checker.options[:hit][:questions].first[:key] = "" |
|
73 |
+ old_questions = @checker.options['hit']['questions'] |
|
74 |
+ @checker.options['hit']['questions'].first['key'] = "" |
|
75 | 75 |
@checker.should_not be_valid |
76 | 76 |
|
77 |
- @checker.options[:hit][:questions] = old_questions |
|
78 |
- @checker.options[:hit][:questions].first[:name] = "" |
|
77 |
+ @checker.options['hit']['questions'] = old_questions |
|
78 |
+ @checker.options['hit']['questions'].first['name'] = "" |
|
79 | 79 |
@checker.should_not be_valid |
80 | 80 |
|
81 |
- @checker.options[:hit][:questions] = old_questions |
|
82 |
- @checker.options[:hit][:questions].first[:required] = nil |
|
81 |
+ @checker.options['hit']['questions'] = old_questions |
|
82 |
+ @checker.options['hit']['questions'].first['required'] = nil |
|
83 | 83 |
@checker.should_not be_valid |
84 | 84 |
|
85 |
- @checker.options[:hit][:questions] = old_questions |
|
86 |
- @checker.options[:hit][:questions].first[:type] = "" |
|
85 |
+ @checker.options['hit']['questions'] = old_questions |
|
86 |
+ @checker.options['hit']['questions'].first['type'] = "" |
|
87 | 87 |
@checker.should_not be_valid |
88 | 88 |
|
89 |
- @checker.options[:hit][:questions] = old_questions |
|
90 |
- @checker.options[:hit][:questions].first[:question] = "" |
|
89 |
+ @checker.options['hit']['questions'] = old_questions |
|
90 |
+ @checker.options['hit']['questions'].first['question'] = "" |
|
91 | 91 |
@checker.should_not be_valid |
92 | 92 |
end |
93 | 93 |
|
94 | 94 |
it "requires that all questions of type 'selection' have a selections array with keys and text" do |
95 |
- @checker.options[:hit][:questions][0][:selections] = [] |
|
95 |
+ @checker.options['hit']['questions'][0]['selections'] = [] |
|
96 | 96 |
@checker.should_not be_valid |
97 |
- @checker.options[:hit][:questions][0][:selections] = [{}] |
|
97 |
+ @checker.options['hit']['questions'][0]['selections'] = [{}] |
|
98 | 98 |
@checker.should_not be_valid |
99 |
- @checker.options[:hit][:questions][0][:selections] = [{ :key => "", :text => "" }] |
|
99 |
+ @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => "", 'text' => "" }] |
|
100 | 100 |
@checker.should_not be_valid |
101 |
- @checker.options[:hit][:questions][0][:selections] = [{ :key => "", :text => "hi" }] |
|
101 |
+ @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => "", 'text' => "hi" }] |
|
102 | 102 |
@checker.should_not be_valid |
103 |
- @checker.options[:hit][:questions][0][:selections] = [{ :key => "hi", :text => "" }] |
|
103 |
+ @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => "hi", 'text' => "" }] |
|
104 | 104 |
@checker.should_not be_valid |
105 |
- @checker.options[:hit][:questions][0][:selections] = [{ :key => "hi", :text => "hi" }] |
|
105 |
+ @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => "hi", 'text' => "hi" }] |
|
106 | 106 |
@checker.should be_valid |
107 |
- @checker.options[:hit][:questions][0][:selections] = [{ :key => "hi", :text => "hi" }, {}] |
|
107 |
+ @checker.options['hit']['questions'][0]['selections'] = [{ 'key' => "hi", 'text' => "hi" }, {}] |
|
108 | 108 |
@checker.should_not be_valid |
109 | 109 |
end |
110 | 110 |
|
111 | 111 |
it "requires that all questions be of type 'selection' when `take_majority` is `true`" do |
112 |
- @checker.options[:take_majority] = "true" |
|
112 |
+ @checker.options['take_majority'] = "true" |
|
113 | 113 |
@checker.should_not be_valid |
114 |
- @checker.options[:hit][:questions][1][:type] = "selection" |
|
115 |
- @checker.options[:hit][:questions][1][:selections] = @checker.options[:hit][:questions][0][:selections] |
|
114 |
+ @checker.options['hit']['questions'][1]['type'] = "selection" |
|
115 |
+ @checker.options['hit']['questions'][1]['selections'] = @checker.options['hit']['questions'][0]['selections'] |
|
116 | 116 |
@checker.should be_valid |
117 | 117 |
end |
118 | 118 |
end |
119 | 119 |
|
120 | 120 |
describe "when 'trigger_on' is set to 'schedule'" do |
121 | 121 |
before do |
122 |
- @checker.options[:trigger_on] = "schedule" |
|
123 |
- @checker.options[:submission_period] = "2" |
|
124 |
- @checker.options.delete(:expected_receive_period_in_days) |
|
122 |
+ @checker.options['trigger_on'] = "schedule" |
|
123 |
+ @checker.options['submission_period'] = "2" |
|
124 |
+ @checker.options.delete('expected_receive_period_in_days') |
|
125 | 125 |
end |
126 | 126 |
|
127 | 127 |
it "should check for reviewable HITs frequently" do |
@@ -151,7 +151,7 @@ describe Agents::HumanTaskAgent do |
||
151 | 151 |
|
152 | 152 |
describe "when 'trigger_on' is set to 'event'" do |
153 | 153 |
it "should not create HITs during check but should check for reviewable HITs" do |
154 |
- @checker.options[:submission_period] = "2" |
|
154 |
+ @checker.options['submission_period'] = "2" |
|
155 | 155 |
now = Time.now |
156 | 156 |
stub(Time).now { now } |
157 | 157 |
mock(@checker).review_hits.times(3) |
@@ -171,9 +171,9 @@ describe Agents::HumanTaskAgent do |
||
171 | 171 |
|
172 | 172 |
describe "creating hits" do |
173 | 173 |
it "can create HITs based on events, interpolating their values" do |
174 |
- @checker.options[:hit][:title] = "Hi <.name>" |
|
175 |
- @checker.options[:hit][:description] = "Make something for <.name>" |
|
176 |
- @checker.options[:hit][:questions][0][:name] = "<.name> Question 1" |
|
174 |
+ @checker.options['hit']['title'] = "Hi <.name>" |
|
175 |
+ @checker.options['hit']['description'] = "Make something for <.name>" |
|
176 |
+ @checker.options['hit']['questions'][0]['name'] = "<.name> Question 1" |
|
177 | 177 |
|
178 | 178 |
question_form = nil |
179 | 179 |
hitInterface = OpenStruct.new |
@@ -183,8 +183,8 @@ describe Agents::HumanTaskAgent do |
||
183 | 183 |
|
184 | 184 |
@checker.send :create_hit, @event |
185 | 185 |
|
186 |
- hitInterface.max_assignments.should == @checker.options[:hit][:assignments] |
|
187 |
- hitInterface.reward.should == @checker.options[:hit][:reward] |
|
186 |
+ hitInterface.max_assignments.should == @checker.options['hit']['assignments'] |
|
187 |
+ hitInterface.reward.should == @checker.options['hit']['reward'] |
|
188 | 188 |
hitInterface.description.should == "Make something for Joe" |
189 | 189 |
|
190 | 190 |
xml = question_form.to_xml |
@@ -192,18 +192,18 @@ describe Agents::HumanTaskAgent do |
||
192 | 192 |
xml.should include("<Text>Make something for Joe</Text>") |
193 | 193 |
xml.should include("<DisplayName>Joe Question 1</DisplayName>") |
194 | 194 |
|
195 |
- @checker.memory[:hits][123].should == @event.id |
|
195 |
+ @checker.memory['hits'][123].should == @event.id |
|
196 | 196 |
end |
197 | 197 |
|
198 | 198 |
it "works without an event too" do |
199 |
- @checker.options[:hit][:title] = "Hi <.name>" |
|
199 |
+ @checker.options['hit']['title'] = "Hi <.name>" |
|
200 | 200 |
hitInterface = OpenStruct.new |
201 | 201 |
hitInterface.id = 123 |
202 | 202 |
mock(hitInterface).question_form(instance_of Agents::HumanTaskAgent::AgentQuestionForm) |
203 | 203 |
mock(RTurk::Hit).create(:title => "Hi").yields(hitInterface) { hitInterface } |
204 | 204 |
@checker.send :create_hit |
205 |
- hitInterface.max_assignments.should == @checker.options[:hit][:assignments] |
|
206 |
- hitInterface.reward.should == @checker.options[:hit][:reward] |
|
205 |
+ hitInterface.max_assignments.should == @checker.options['hit']['assignments'] |
|
206 |
+ hitInterface.reward.should == @checker.options['hit']['reward'] |
|
207 | 207 |
end |
208 | 208 |
end |
209 | 209 |
|
@@ -253,14 +253,14 @@ describe Agents::HumanTaskAgent do |
||
253 | 253 |
it "should work on multiple HITs" do |
254 | 254 |
event2 = Event.new |
255 | 255 |
event2.agent = agents(:bob_rain_notifier_agent) |
256 |
- event2.payload = { :foo2 => { "bar2" => { :baz2 => "a2b2" } }, |
|
257 |
- :name2 => "Joe2" } |
|
256 |
+ event2.payload = { 'foo2' => { "bar2" => { 'baz2' => "a2b2" } }, |
|
257 |
+ 'name2' => "Joe2" } |
|
258 | 258 |
event2.id = 3452 |
259 | 259 |
|
260 | 260 |
# It knows about two HITs from two different events. |
261 |
- @checker.memory[:hits] = {} |
|
262 |
- @checker.memory[:hits][:"JH3132836336DHG"] = @event.id |
|
263 |
- @checker.memory[:hits][:"JH39AA63836DHG"] = event2.id |
|
261 |
+ @checker.memory['hits'] = {} |
|
262 |
+ @checker.memory['hits']["JH3132836336DHG"] = @event.id |
|
263 |
+ @checker.memory['hits']["JH39AA63836DHG"] = event2.id |
|
264 | 264 |
|
265 | 265 |
hit_ids = %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345] |
266 | 266 |
mock(RTurk::GetReviewableHITs).create { mock!.hit_ids { hit_ids } } # It sees 3 HITs. |
@@ -273,7 +273,7 @@ describe Agents::HumanTaskAgent do |
||
273 | 273 |
end |
274 | 274 |
|
275 | 275 |
it "shouldn't do anything if an assignment isn't ready" do |
276 |
- @checker.memory[:hits] = { :"JH3132836336DHG" => @event.id } |
|
276 |
+ @checker.memory['hits'] = { "JH3132836336DHG" => @event.id } |
|
277 | 277 |
mock(RTurk::GetReviewableHITs).create { mock!.hit_ids { %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345] } } |
278 | 278 |
assignments = [ |
279 | 279 |
FakeAssignment.new(:status => "Accepted", :answers => {}), |
@@ -288,11 +288,11 @@ describe Agents::HumanTaskAgent do |
||
288 | 288 |
@checker.send :review_hits |
289 | 289 |
|
290 | 290 |
assignments.all? {|a| a.approved == true }.should be_false |
291 |
- @checker.memory[:hits].should == { "JH3132836336DHG" => @event.id } |
|
291 |
+ @checker.memory['hits'].should == { "JH3132836336DHG" => @event.id } |
|
292 | 292 |
end |
293 | 293 |
|
294 | 294 |
it "shouldn't do anything if an assignment is missing" do |
295 |
- @checker.memory[:hits] = { :"JH3132836336DHG" => @event.id } |
|
295 |
+ @checker.memory['hits'] = { "JH3132836336DHG" => @event.id } |
|
296 | 296 |
mock(RTurk::GetReviewableHITs).create { mock!.hit_ids { %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345] } } |
297 | 297 |
assignments = [ |
298 | 298 |
FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"happy", "feedback"=>"Take 2"}) |
@@ -306,11 +306,11 @@ describe Agents::HumanTaskAgent do |
||
306 | 306 |
@checker.send :review_hits |
307 | 307 |
|
308 | 308 |
assignments.all? {|a| a.approved == true }.should be_false |
309 |
- @checker.memory[:hits].should == { "JH3132836336DHG" => @event.id } |
|
309 |
+ @checker.memory['hits'].should == { "JH3132836336DHG" => @event.id } |
|
310 | 310 |
end |
311 | 311 |
|
312 | 312 |
it "should create events when all assignments are ready" do |
313 |
- @checker.memory[:hits] = { :"JH3132836336DHG" => @event.id } |
|
313 |
+ @checker.memory['hits'] = { "JH3132836336DHG" => @event.id } |
|
314 | 314 |
mock(RTurk::GetReviewableHITs).create { mock!.hit_ids { %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345] } } |
315 | 315 |
assignments = [ |
316 | 316 |
FakeAssignment.new(:status => "Submitted", :answers => {"sentiment"=>"neutral", "feedback"=>""}), |
@@ -327,32 +327,32 @@ describe Agents::HumanTaskAgent do |
||
327 | 327 |
assignments.all? {|a| a.approved == true }.should be_true |
328 | 328 |
hit.should be_disposed |
329 | 329 |
|
330 |
- @checker.events.last.payload[:answers].should == [ |
|
330 |
+ @checker.events.last.payload['answers'].should == [ |
|
331 | 331 |
{'sentiment' => "neutral", 'feedback' => ""}, |
332 | 332 |
{'sentiment' => "happy", 'feedback' => "Take 2"} |
333 | 333 |
] |
334 | 334 |
|
335 |
- @checker.memory[:hits].should == {} |
|
335 |
+ @checker.memory['hits'].should == {} |
|
336 | 336 |
end |
337 | 337 |
|
338 | 338 |
describe "taking majority votes" do |
339 | 339 |
before do |
340 |
- @checker.options[:take_majority] = "true" |
|
341 |
- @checker.memory[:hits] = { "JH3132836336DHG" => @event.id } |
|
340 |
+ @checker.options['take_majority'] = "true" |
|
341 |
+ @checker.memory['hits'] = { "JH3132836336DHG" => @event.id } |
|
342 | 342 |
mock(RTurk::GetReviewableHITs).create { mock!.hit_ids { %w[JH3132836336DHG JH39AA63836DHG JH39AA63836DH12345] } } |
343 | 343 |
end |
344 | 344 |
|
345 | 345 |
it "should take the majority votes of all questions" do |
346 |
- @checker.options[:hit][:questions][1] = { |
|
347 |
- :type => "selection", |
|
348 |
- :key => "age_range", |
|
349 |
- :name => "Age Range", |
|
350 |
- :required => "true", |
|
351 |
- :question => "Please select your age range:", |
|
352 |
- :selections => |
|
346 |
+ @checker.options['hit']['questions'][1] = { |
|
347 |
+ 'type' => "selection", |
|
348 |
+ 'key' => "age_range", |
|
349 |
+ 'name' => "Age Range", |
|
350 |
+ 'required' => "true", |
|
351 |
+ 'question' => "Please select your age range:", |
|
352 |
+ 'selections' => |
|
353 | 353 |
[ |
354 |
- { :key => "<50", :text => "50 years old or younger" }, |
|
355 |
- { :key => ">50", :text => "Over 50 years old" } |
|
354 |
+ { 'key' => "<50", 'text' => "50 years old or younger" }, |
|
355 |
+ { 'key' => ">50", 'text' => "Over 50 years old" } |
|
356 | 356 |
] |
357 | 357 |
} |
358 | 358 |
|
@@ -371,35 +371,35 @@ describe Agents::HumanTaskAgent do |
||
371 | 371 |
|
372 | 372 |
assignments.all? {|a| a.approved == true }.should be_true |
373 | 373 |
|
374 |
- @checker.events.last.payload[:answers].should == [ |
|
374 |
+ @checker.events.last.payload['answers'].should == [ |
|
375 | 375 |
{ 'sentiment' => "sad", 'age_range' => "<50" }, |
376 | 376 |
{ 'sentiment' => "neutral", 'age_range' => ">50" }, |
377 | 377 |
{ 'sentiment' => "happy", 'age_range' => ">50" }, |
378 | 378 |
{ 'sentiment' => "happy", 'age_range' => ">50" } |
379 | 379 |
] |
380 | 380 |
|
381 |
- @checker.events.last.payload[:counts].should == { 'sentiment' => { 'happy' => 2, 'sad' => 1, 'neutral' => 1 }, 'age_range' => { ">50" => 3, "<50" => 1 } } |
|
382 |
- @checker.events.last.payload[:majority_answer].should == { 'sentiment' => "happy", 'age_range' => ">50" } |
|
383 |
- @checker.events.last.payload.should_not have_key(:average_answer) |
|
381 |
+ @checker.events.last.payload['counts'].should == { 'sentiment' => { 'happy' => 2, 'sad' => 1, 'neutral' => 1 }, 'age_range' => { ">50" => 3, "<50" => 1 } } |
|
382 |
+ @checker.events.last.payload['majority_answer'].should == { 'sentiment' => "happy", 'age_range' => ">50" } |
|
383 |
+ @checker.events.last.payload.should_not have_key('average_answer') |
|
384 | 384 |
|
385 |
- @checker.memory[:hits].should == {} |
|
385 |
+ @checker.memory['hits'].should == {} |
|
386 | 386 |
end |
387 | 387 |
|
388 | 388 |
it "should also provide an average answer when all questions are numeric" do |
389 |
- @checker.options[:hit][:questions] = [ |
|
389 |
+ @checker.options['hit']['questions'] = [ |
|
390 | 390 |
{ |
391 |
- :type => "selection", |
|
392 |
- :key => "rating", |
|
393 |
- :name => "Rating", |
|
394 |
- :required => "true", |
|
395 |
- :question => "Please select a rating:", |
|
396 |
- :selections => |
|
391 |
+ 'type' => "selection", |
|
392 |
+ 'key' => "rating", |
|
393 |
+ 'name' => "Rating", |
|
394 |
+ 'required' => "true", |
|
395 |
+ 'question' => "Please select a rating:", |
|
396 |
+ 'selections' => |
|
397 | 397 |
[ |
398 |
- { :key => "1", :text => "One" }, |
|
399 |
- { :key => "2", :text => "Two" }, |
|
400 |
- { :key => "3", :text => "Three" }, |
|
401 |
- { :key => "4", :text => "Four" }, |
|
402 |
- { :key => "5.1", :text => "Five Point One" } |
|
398 |
+ { 'key' => "1", 'text' => "One" }, |
|
399 |
+ { 'key' => "2", 'text' => "Two" }, |
|
400 |
+ { 'key' => "3", 'text' => "Three" }, |
|
401 |
+ { 'key' => "4", 'text' => "Four" }, |
|
402 |
+ { 'key' => "5.1", 'text' => "Five Point One" } |
|
403 | 403 |
] |
404 | 404 |
} |
405 | 405 |
] |
@@ -420,7 +420,7 @@ describe Agents::HumanTaskAgent do |
||
420 | 420 |
|
421 | 421 |
assignments.all? {|a| a.approved == true }.should be_true |
422 | 422 |
|
423 |
- @checker.events.last.payload[:answers].should == [ |
|
423 |
+ @checker.events.last.payload['answers'].should == [ |
|
424 | 424 |
{ 'rating' => "1" }, |
425 | 425 |
{ 'rating' => "3" }, |
426 | 426 |
{ 'rating' => "5.1" }, |
@@ -428,11 +428,11 @@ describe Agents::HumanTaskAgent do |
||
428 | 428 |
{ 'rating' => "2" } |
429 | 429 |
] |
430 | 430 |
|
431 |
- @checker.events.last.payload[:counts].should == { 'rating' => { "1" => 1, "2" => 2, "3" => 1, "4" => 0, "5.1" => 1 } } |
|
432 |
- @checker.events.last.payload[:majority_answer].should == { 'rating' => "2" } |
|
433 |
- @checker.events.last.payload[:average_answer].should == { 'rating' => (1 + 2 + 2 + 3 + 5.1) / 5.0 } |
|
431 |
+ @checker.events.last.payload['counts'].should == { 'rating' => { "1" => 1, "2" => 2, "3" => 1, "4" => 0, "5.1" => 1 } } |
|
432 |
+ @checker.events.last.payload['majority_answer'].should == { 'rating' => "2" } |
|
433 |
+ @checker.events.last.payload['average_answer'].should == { 'rating' => (1 + 2 + 2 + 3 + 5.1) / 5.0 } |
|
434 | 434 |
|
435 |
- @checker.memory[:hits].should == {} |
|
435 |
+ @checker.memory['hits'].should == {} |
|
436 | 436 |
end |
437 | 437 |
end |
438 | 438 |
end |
@@ -3,12 +3,12 @@ require 'spec_helper' |
||
3 | 3 |
describe Agents::PeakDetectorAgent do |
4 | 4 |
before do |
5 | 5 |
@valid_params = { |
6 |
- :name => "my peak detector agent", |
|
7 |
- :options => { |
|
8 |
- :expected_receive_period_in_days => "2", |
|
9 |
- :group_by_path => "filter", |
|
10 |
- :value_path => "count", |
|
11 |
- :message => "A peak was found" |
|
6 |
+ 'name' => "my peak detector agent", |
|
7 |
+ 'options' => { |
|
8 |
+ 'expected_receive_period_in_days' => "2", |
|
9 |
+ 'group_by_path' => "filter", |
|
10 |
+ 'value_path' => "count", |
|
11 |
+ 'message' => "A peak was found" |
|
12 | 12 |
} |
13 | 13 |
} |
14 | 14 |
|
@@ -19,54 +19,54 @@ describe Agents::PeakDetectorAgent do |
||
19 | 19 |
|
20 | 20 |
describe "#receive" do |
21 | 21 |
it "tracks and groups by the group_by_path" do |
22 |
- events = build_events(:keys => [:count, :filter], |
|
22 |
+ events = build_events(:keys => ['count', 'filter'], |
|
23 | 23 |
:values => [[1, "something"], [2, "something"], [3, "else"]]) |
24 | 24 |
@agent.receive events |
25 |
- @agent.memory[:data][:something].map(&:first).should == [1, 2] |
|
26 |
- @agent.memory[:data][:something].last.last.should be_within(10).of((100 - 1).hours.ago.to_i) |
|
27 |
- @agent.memory[:data][:else].first.first.should == 3 |
|
28 |
- @agent.memory[:data][:else].first.last.should be_within(10).of((100 - 2).hours.ago.to_i) |
|
25 |
+ @agent.memory['data']['something'].map(&:first).should == [1, 2] |
|
26 |
+ @agent.memory['data']['something'].last.last.should be_within(10).of((100 - 1).hours.ago.to_i) |
|
27 |
+ @agent.memory['data']['else'].first.first.should == 3 |
|
28 |
+ @agent.memory['data']['else'].first.last.should be_within(10).of((100 - 2).hours.ago.to_i) |
|
29 | 29 |
end |
30 | 30 |
|
31 | 31 |
it "works without a group_by_path as well" do |
32 |
- @agent.options[:group_by_path] = "" |
|
33 |
- events = build_events(:keys => [:count], :values => [[1], [2]]) |
|
32 |
+ @agent.options['group_by_path'] = "" |
|
33 |
+ events = build_events(:keys => ['count'], :values => [[1], [2]]) |
|
34 | 34 |
@agent.receive events |
35 |
- @agent.memory[:data][:no_group].map(&:first).should == [1, 2] |
|
35 |
+ @agent.memory['data']['no_group'].map(&:first).should == [1, 2] |
|
36 | 36 |
end |
37 | 37 |
|
38 | 38 |
it "keeps a rolling window of data" do |
39 |
- @agent.options[:window_duration_in_days] = 5/24.0 |
|
40 |
- @agent.receive build_events(:keys => [:count], |
|
39 |
+ @agent.options['window_duration_in_days'] = 5/24.0 |
|
40 |
+ @agent.receive build_events(:keys => ['count'], |
|
41 | 41 |
:values => [1, 2, 3, 4, 5, 6, 7, 8].map {|i| [i]}, |
42 |
- :pattern => { :filter => "something" }) |
|
43 |
- @agent.memory[:data][:something].map(&:first).should == [4, 5, 6, 7, 8] |
|
42 |
+ :pattern => { 'filter' => "something" }) |
|
43 |
+ @agent.memory['data']['something'].map(&:first).should == [4, 5, 6, 7, 8] |
|
44 | 44 |
end |
45 | 45 |
|
46 | 46 |
it "finds peaks" do |
47 |
- build_events(:keys => [:count], |
|
47 |
+ build_events(:keys => ['count'], |
|
48 | 48 |
:values => [5, 6, |
49 | 49 |
4, 5, |
50 | 50 |
4, 5, |
51 | 51 |
15, 11, # peak |
52 | 52 |
8, 50, # ignored because it's too close to the first peak |
53 | 53 |
4, 5].map {|i| [i]}, |
54 |
- :pattern => { :filter => "something" }).each.with_index do |event, index| |
|
54 |
+ :pattern => { 'filter' => "something" }).each.with_index do |event, index| |
|
55 | 55 |
lambda { |
56 | 56 |
@agent.receive([event]) |
57 | 57 |
}.should change { @agent.events.count }.by( index == 6 ? 1 : 0 ) |
58 | 58 |
end |
59 | 59 |
|
60 |
- @agent.events.last.payload[:peak].should == 15.0 |
|
61 |
- @agent.memory[:peaks][:something].length.should == 1 |
|
60 |
+ @agent.events.last.payload['peak'].should == 15.0 |
|
61 |
+ @agent.memory['peaks']['something'].length.should == 1 |
|
62 | 62 |
end |
63 | 63 |
|
64 | 64 |
it "keeps a rolling window of peaks" do |
65 |
- @agent.options[:min_peak_spacing_in_days] = 1/24.0 |
|
66 |
- @agent.receive build_events(:keys => [:count], |
|
65 |
+ @agent.options['min_peak_spacing_in_days'] = 1/24.0 |
|
66 |
+ @agent.receive build_events(:keys => ['count'], |
|
67 | 67 |
:values => [1, 1, 1, 1, 1, 1, 10, 1, 1, 1, 1, 1, 1, 1, 10, 1].map {|i| [i]}, |
68 |
- :pattern => { :filter => "something" }) |
|
69 |
- @agent.memory[:peaks][:something].length.should == 2 |
|
68 |
+ :pattern => { 'filter' => "something" }) |
|
69 |
+ @agent.memory['peaks']['something'].length.should == 2 |
|
70 | 70 |
end |
71 | 71 |
end |
72 | 72 |
|
@@ -76,17 +76,17 @@ describe Agents::PeakDetectorAgent do |
||
76 | 76 |
end |
77 | 77 |
|
78 | 78 |
it "should validate presence of message" do |
79 |
- @agent.options[:message] = nil |
|
79 |
+ @agent.options['message'] = nil |
|
80 | 80 |
@agent.should_not be_valid |
81 | 81 |
end |
82 | 82 |
|
83 | 83 |
it "should validate presence of expected_receive_period_in_days" do |
84 |
- @agent.options[:expected_receive_period_in_days] = "" |
|
84 |
+ @agent.options['expected_receive_period_in_days'] = "" |
|
85 | 85 |
@agent.should_not be_valid |
86 | 86 |
end |
87 | 87 |
|
88 | 88 |
it "should validate presence of value_path" do |
89 |
- @agent.options[:value_path] = "" |
|
89 |
+ @agent.options['value_path'] = "" |
|
90 | 90 |
@agent.should_not be_valid |
91 | 91 |
end |
92 | 92 |
end |
@@ -3,16 +3,16 @@ require 'spec_helper' |
||
3 | 3 |
describe Agents::TriggerAgent do |
4 | 4 |
before do |
5 | 5 |
@valid_params = { |
6 |
- :name => "my trigger agent", |
|
7 |
- :options => { |
|
8 |
- :expected_receive_period_in_days => 2, |
|
9 |
- :rules => [{ |
|
10 |
- :type => "regex", |
|
11 |
- 'value' => "a\\db", |
|
12 |
- :path => "foo.bar.baz", |
|
13 |
- }], |
|
14 |
- :message => "I saw '<foo.bar.baz>' from <name>" |
|
15 |
- } |
|
6 |
+ 'name' => "my trigger agent", |
|
7 |
+ 'options' => { |
|
8 |
+ 'expected_receive_period_in_days' => 2, |
|
9 |
+ 'rules' => [{ |
|
10 |
+ 'type' => "regex", |
|
11 |
+ 'value' => "a\\db", |
|
12 |
+ 'path' => "foo.bar.baz", |
|
13 |
+ }], |
|
14 |
+ 'message' => "I saw '<foo.bar.baz>' from <name>" |
|
15 |
+ } |
|
16 | 16 |
} |
17 | 17 |
|
18 | 18 |
@checker = Agents::TriggerAgent.new(@valid_params) |
@@ -21,8 +21,8 @@ describe Agents::TriggerAgent do |
||
21 | 21 |
|
22 | 22 |
@event = Event.new |
23 | 23 |
@event.agent = agents(:bob_rain_notifier_agent) |
24 |
- @event.payload = { :foo => { "bar" => { :baz => "a2b" }}, |
|
25 |
- :name => "Joe" } |
|
24 |
+ @event.payload = { 'foo' => { "bar" => { 'baz' => "a2b" }}, |
|
25 |
+ 'name' => "Joe" } |
|
26 | 26 |
end |
27 | 27 |
|
28 | 28 |
describe "validation" do |
@@ -31,22 +31,22 @@ describe Agents::TriggerAgent do |
||
31 | 31 |
end |
32 | 32 |
|
33 | 33 |
it "should validate presence of options" do |
34 |
- @checker.options[:message] = nil |
|
34 |
+ @checker.options['message'] = nil |
|
35 | 35 |
@checker.should_not be_valid |
36 | 36 |
end |
37 | 37 |
|
38 | 38 |
it "should validate the three fields in each rule" do |
39 |
- @checker.options[:rules] << { :path => "foo", :type => "fake", :value => "6" } |
|
39 |
+ @checker.options['rules'] << { 'path' => "foo", 'type' => "fake", 'value' => "6" } |
|
40 | 40 |
@checker.should_not be_valid |
41 |
- @checker.options[:rules].last[:type] = "field>=value" |
|
41 |
+ @checker.options['rules'].last['type'] = "field>=value" |
|
42 | 42 |
@checker.should be_valid |
43 |
- @checker.options[:rules].last.delete(:value) |
|
43 |
+ @checker.options['rules'].last.delete('value') |
|
44 | 44 |
@checker.should_not be_valid |
45 | 45 |
end |
46 | 46 |
end |
47 | 47 |
|
48 | 48 |
describe "#working?" do |
49 |
- it "checks to see if the Agent has received any events in the last :expected_receive_period_in_days days" do |
|
49 |
+ it "checks to see if the Agent has received any events in the last 'expected_receive_period_in_days' days" do |
|
50 | 50 |
@event.save! |
51 | 51 |
|
52 | 52 |
@checker.should_not be_working # no events have ever been received |
@@ -60,30 +60,30 @@ describe Agents::TriggerAgent do |
||
60 | 60 |
|
61 | 61 |
describe "#receive" do |
62 | 62 |
it "handles regex" do |
63 |
- @event.payload[:foo]["bar"][:baz] = "a222b" |
|
63 |
+ @event.payload['foo']['bar']['baz'] = "a222b" |
|
64 | 64 |
lambda { |
65 | 65 |
@checker.receive([@event]) |
66 | 66 |
}.should_not change { Event.count } |
67 | 67 |
|
68 |
- @event.payload[:foo]["bar"][:baz] = "a2b" |
|
68 |
+ @event.payload['foo']['bar']['baz'] = "a2b" |
|
69 | 69 |
lambda { |
70 | 70 |
@checker.receive([@event]) |
71 | 71 |
}.should change { Event.count }.by(1) |
72 | 72 |
end |
73 | 73 |
|
74 | 74 |
it "handles negated regex" do |
75 |
- @event.payload[:foo]["bar"][:baz] = "a2b" |
|
76 |
- @checker.options[:rules][0] = { |
|
77 |
- :type => "!regex", |
|
78 |
- :value => "a\\db", |
|
79 |
- :path => "foo.bar.baz", |
|
80 |
- } |
|
75 |
+ @event.payload['foo']['bar']['baz'] = "a2b" |
|
76 |
+ @checker.options['rules'][0] = { |
|
77 |
+ 'type' => "!regex", |
|
78 |
+ 'value' => "a\\db", |
|
79 |
+ 'path' => "foo.bar.baz", |
|
80 |
+ } |
|
81 | 81 |
|
82 | 82 |
lambda { |
83 | 83 |
@checker.receive([@event]) |
84 | 84 |
}.should_not change { Event.count } |
85 | 85 |
|
86 |
- @event.payload[:foo]["bar"][:baz] = "a22b" |
|
86 |
+ @event.payload['foo']['bar']['baz'] = "a22b" |
|
87 | 87 |
lambda { |
88 | 88 |
@checker.receive([@event]) |
89 | 89 |
}.should change { Event.count }.by(1) |
@@ -91,49 +91,49 @@ describe Agents::TriggerAgent do |
||
91 | 91 |
|
92 | 92 |
it "puts can extract values into the message based on paths" do |
93 | 93 |
@checker.receive([@event]) |
94 |
- Event.last.payload[:message].should == "I saw 'a2b' from Joe" |
|
94 |
+ Event.last.payload['message'].should == "I saw 'a2b' from Joe" |
|
95 | 95 |
end |
96 | 96 |
|
97 | 97 |
it "handles numerical comparisons" do |
98 |
- @event.payload[:foo]["bar"][:baz] = "5" |
|
99 |
- @checker.options[:rules].first[:value] = 6 |
|
100 |
- @checker.options[:rules].first[:type] = "field<value" |
|
98 |
+ @event.payload['foo']['bar']['baz'] = "5" |
|
99 |
+ @checker.options['rules'].first['value'] = 6 |
|
100 |
+ @checker.options['rules'].first['type'] = "field<value" |
|
101 | 101 |
|
102 | 102 |
lambda { |
103 | 103 |
@checker.receive([@event]) |
104 | 104 |
}.should change { Event.count }.by(1) |
105 | 105 |
|
106 |
- @checker.options[:rules].first[:value] = 3 |
|
106 |
+ @checker.options['rules'].first['value'] = 3 |
|
107 | 107 |
lambda { |
108 | 108 |
@checker.receive([@event]) |
109 | 109 |
}.should_not change { Event.count } |
110 | 110 |
end |
111 | 111 |
|
112 | 112 |
it "handles exact comparisons" do |
113 |
- @event.payload[:foo]["bar"][:baz] = "hello world" |
|
114 |
- @checker.options[:rules].first[:type] = "field==value" |
|
113 |
+ @event.payload['foo']['bar']['baz'] = "hello world" |
|
114 |
+ @checker.options['rules'].first['type'] = "field==value" |
|
115 | 115 |
|
116 |
- @checker.options[:rules].first[:value] = "hello there" |
|
116 |
+ @checker.options['rules'].first['value'] = "hello there" |
|
117 | 117 |
lambda { |
118 | 118 |
@checker.receive([@event]) |
119 | 119 |
}.should_not change { Event.count } |
120 | 120 |
|
121 |
- @checker.options[:rules].first[:value] = "hello world" |
|
121 |
+ @checker.options['rules'].first['value'] = "hello world" |
|
122 | 122 |
lambda { |
123 | 123 |
@checker.receive([@event]) |
124 | 124 |
}.should change { Event.count }.by(1) |
125 | 125 |
end |
126 | 126 |
|
127 | 127 |
it "handles negated comparisons" do |
128 |
- @event.payload[:foo]["bar"][:baz] = "hello world" |
|
129 |
- @checker.options[:rules].first[:type] = "field!=value" |
|
130 |
- @checker.options[:rules].first[:value] = "hello world" |
|
128 |
+ @event.payload['foo']['bar']['baz'] = "hello world" |
|
129 |
+ @checker.options['rules'].first['type'] = "field!=value" |
|
130 |
+ @checker.options['rules'].first['value'] = "hello world" |
|
131 | 131 |
|
132 | 132 |
lambda { |
133 | 133 |
@checker.receive([@event]) |
134 | 134 |
}.should_not change { Event.count } |
135 | 135 |
|
136 |
- @checker.options[:rules].first[:value] = "hello there" |
|
136 |
+ @checker.options['rules'].first['value'] = "hello there" |
|
137 | 137 |
|
138 | 138 |
lambda { |
139 | 139 |
@checker.receive([@event]) |
@@ -141,20 +141,20 @@ describe Agents::TriggerAgent do |
||
141 | 141 |
end |
142 | 142 |
|
143 | 143 |
it "does fine without dots in the path" do |
144 |
- @event.payload = { :hello => "world" } |
|
145 |
- @checker.options[:rules].first[:type] = "field==value" |
|
146 |
- @checker.options[:rules].first[:path] = "hello" |
|
147 |
- @checker.options[:rules].first[:value] = "world" |
|
144 |
+ @event.payload = { 'hello' => "world" } |
|
145 |
+ @checker.options['rules'].first['type'] = "field==value" |
|
146 |
+ @checker.options['rules'].first['path'] = "hello" |
|
147 |
+ @checker.options['rules'].first['value'] = "world" |
|
148 | 148 |
lambda { |
149 | 149 |
@checker.receive([@event]) |
150 | 150 |
}.should change { Event.count }.by(1) |
151 | 151 |
|
152 |
- @checker.options[:rules].first[:path] = "foo" |
|
152 |
+ @checker.options['rules'].first['path'] = "foo" |
|
153 | 153 |
lambda { |
154 | 154 |
@checker.receive([@event]) |
155 | 155 |
}.should_not change { Event.count } |
156 | 156 |
|
157 |
- @checker.options[:rules].first[:value] = "hi" |
|
157 |
+ @checker.options['rules'].first['value'] = "hi" |
|
158 | 158 |
lambda { |
159 | 159 |
@checker.receive([@event]) |
160 | 160 |
}.should_not change { Event.count } |
@@ -163,11 +163,11 @@ describe Agents::TriggerAgent do |
||
163 | 163 |
it "handles multiple events" do |
164 | 164 |
event2 = Event.new |
165 | 165 |
event2.agent = agents(:bob_weather_agent) |
166 |
- event2.payload = { :foo => { "bar" => { :baz => "a2b" }}} |
|
166 |
+ event2.payload = { 'foo' => { 'bar' => { 'baz' => "a2b" }}} |
|
167 | 167 |
|
168 | 168 |
event3 = Event.new |
169 | 169 |
event3.agent = agents(:bob_weather_agent) |
170 |
- event3.payload = { :foo => { "bar" => { :baz => "a222b" }}} |
|
170 |
+ event3.payload = { 'foo' => { 'bar' => { 'baz' => "a222b" }}} |
|
171 | 171 |
|
172 | 172 |
lambda { |
173 | 173 |
@checker.receive([@event, event2, event3]) |
@@ -175,19 +175,19 @@ describe Agents::TriggerAgent do |
||
175 | 175 |
end |
176 | 176 |
|
177 | 177 |
it "handles ANDing rules together" do |
178 |
- @checker.options[:rules] << { |
|
179 |
- :type => "field>=value", |
|
180 |
- :value => "4", |
|
181 |
- :path => "foo.bing" |
|
178 |
+ @checker.options['rules'] << { |
|
179 |
+ 'type' => "field>=value", |
|
180 |
+ 'value' => "4", |
|
181 |
+ 'path' => "foo.bing" |
|
182 | 182 |
} |
183 | 183 |
|
184 |
- @event.payload[:foo]["bing"] = "5" |
|
184 |
+ @event.payload['foo']["bing"] = "5" |
|
185 | 185 |
|
186 | 186 |
lambda { |
187 | 187 |
@checker.receive([@event]) |
188 | 188 |
}.should change { Event.count }.by(1) |
189 | 189 |
|
190 |
- @checker.options[:rules].last[:value] = 6 |
|
190 |
+ @checker.options['rules'].last['value'] = 6 |
|
191 | 191 |
lambda { |
192 | 192 |
@checker.receive([@event]) |
193 | 193 |
}.should_not change { Event.count } |